{
  "nbformat": 4,
  "nbformat_minor": 5,
  "metadata": {
    "kernelspec": {
      "name": "aner",
      "display_name": "Aner",
      "language": "aner"
    },
    "language_info": {
      "name": "aner",
      "file_extension": ".aner"
    },
    "aner": {
      "execution_mode": "persistent-session",
      "runtime_mode": "development"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "# Tensor precision and device placement\n\nCPU Float32 and available MPS/CUDA executions use the same seeded tensors. Device availability is explicit. Each run performs eight SGD updates and returns trained resident weights. Per device cells keep observations within the bounded Dev capture budget. Notebook observations appear after the cell completes.\n",
      "id": "gpu-introduction"
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "// Explicit Tensor precision and placement; no silent device selection.\n",
        "import aner.nn\n",
        "import aner.dev\n",
        "\n",
        "fn exercise(device: String) -> Tensor {\n",
        "    nn.require_device(device)\n",
        "    let input = nn.random(32, 64, seed: 2026, scale: 0.25)\n",
        "        .astype(dtype: \"Float32\").to(device: device)\n",
        "    var weights = nn.parameter(nn.random(64, 16, seed: 42, scale: 0.1)\n",
        "        .astype(dtype: \"Float32\").to(device: device))\n",
        "    let target = nn.zeros(32, 16).astype(dtype: \"Float32\").to(device: device)\n",
        "    let first = nn.mse(nn.matmul(input, weights), target)\n",
        "    print(device)\n",
        "    print(weights.dtype())\n",
        "    print(weights.backend())\n",
        "    print(nn.item(first))\n",
        "    var step = 0\n",
        "    while step < 8 {\n",
        "        let loss = nn.mse(nn.matmul(input, weights), target)\n",
        "        nn.backward(loss)\n",
        "        weights = nn.sgd(weights, rate: 0.1)\n",
        "        step = step + 1\n",
        "    }\n",
        "    nn.synchronize(device)\n",
        "    let final = nn.mse(nn.matmul(input, weights), target)\n",
        "    print(nn.item(final))\n",
        "    dev.capture(\"resident trained weights\", weights)\n",
        "    let portable = nn.detach(weights).to(device: \"cpu\")\n",
        "    print(portable.device())\n",
        "    print(portable.dtype())\n",
        "    return nn.detach(weights)\n",
        "}\n",
        "\n",
        "print(nn.devices())\n"
      ],
      "execution_count": null,
      "outputs": [],
      "id": "gpu-0"
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "let cpu_weights = exercise(\"cpu\")\n"
      ],
      "execution_count": null,
      "outputs": [],
      "id": "gpu-1"
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "var mps_weights = nn.zeros(1, 1).astype(dtype: \"Float32\")\n",
        "if nn.device_available(\"mps\") { mps_weights = exercise(\"mps\") }\n",
        "else { print(\"MPS unavailable: this build or device does not support it\") }\n"
      ],
      "execution_count": null,
      "outputs": [],
      "id": "gpu-2"
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "var cuda_weights = nn.zeros(1, 1).astype(dtype: \"Float32\")\n",
        "if nn.device_available(\"cuda\") { cuda_weights = exercise(\"cuda\") }\n",
        "else { print(\"CUDA unavailable: this build or device does not support it\") }\n"
      ],
      "execution_count": null,
      "outputs": [],
      "id": "gpu-3"
    }
  ]
}
