defmodule ExBurn do @moduledoc """ ExBurn — Elixir bridge to the [Burn](https://burn.dev) deep learning framework. ExBurn provides a high-level API for tensor computation, neural network training, and GPU-accelerated machine learning by delegating to Burn via Rust NIFs (Native Implemented Functions). ## Architecture ``` Elixir/Axon → Nx.Defn → ExBurn.Defn.Compiler → ExBurn.Backend → ExBurn.Nif (Rustler) → Burn/CubeCL → GPU ↕ ExCubecl (GPU buffers, kernels, pipelines) ``` ## Quick Start # Set ExBurn as the default Nx backend Nx.default_backend(ExBurn.Backend) # Create and manipulate tensors t = Nx.tensor([1.0, 2.0, 3.0]) Nx.add(t, t) |> Nx.to_list() ## Modules - `ExBurn.Defn.Compiler` — `Nx.Defn.Compiler` implementation for GPU-accelerated defn - `ExBurn.Backend` — Nx backend that delegates to Burn via NIF - `ExBurn.Nif` — Rustler NIF stubs for Burn interop - `ExBurn.Tensor` — Tensor conversion utilities between Nx and Burn formats - `ExBurn.BurnBridge` — High-level bridge for Burn operations and ExCubecl buffers - `ExBurn.CubeclBridge` — GPU compute via ExCubecl (buffers, kernels, pipelines) - `ExBurn.Model` — Model definition and training orchestration - `ExBurn.Training` — Training loop implementation - `ExBurn.Serving` — Nx.Serving integration for batched concurrent inference """ @doc "Returns the current version of ExBurn." @spec version() :: String.t() def version, do: Application.spec(:ex_burn, :vsn) |> to_string() @doc """ Returns the default device for tensor operations. Currently returns `:gpu` when a compatible GPU backend is available, otherwise falls back to `:cpu`. """ @spec default_device() :: :cpu | :gpu def default_device do if ExBurn.NifHelper.gpu_available(), do: :gpu, else: :cpu end @doc """ Returns the name of the active compute device (e.g., "CUDA (NVIDIA GPU)"). """ @spec device_name() :: String.t() def device_name do ExBurn.BurnBridge.device_name() end @doc """ Returns a map with device information including GPU availability, backend name, and available backends. """ @spec device_info() :: map() def device_info do ExBurn.BurnBridge.device_info() end @doc """ Checks whether an NVIDIA CUDA GPU is available. """ @spec cuda_available?() :: boolean() def cuda_available? do ExBurn.CubeclBridge.cuda_available?() end @doc """ Sets the default Nx backend to `ExBurn.Backend`. After calling this, all Nx operations will be executed via Burn. """ @spec configure!() :: :ok def configure! do Nx.default_backend(ExBurn.Backend) :ok end @doc """ Returns a summary of the ExBurn environment. Includes version, device info, GPU availability, and available backends. ## Example IO.puts(ExBurn.summary()) """ @spec summary() :: String.t() def summary do device = device_name() gpu? = cuda_available?() backends = ExBurn.CubeclBridge.available_backends() """ ExBurn v#{version()} ────────────────────────────── Device: #{device} GPU: #{if gpu?, do: "available", else: "not available"} Backends: #{Enum.join(Enum.map(backends, &Atom.to_string/1), ", ")} """ |> String.trim() end @doc """ Checks whether the NIF library is loaded and functional. Returns `true` if the NIF responds to a basic health check, `false` otherwise. """ @spec nif_loaded?() :: boolean() def nif_loaded? do try do apply(ExBurn.Nif, :gpu_available, []) true rescue _ -> false end end @doc """ Returns the number of NIF functions registered by the Rust library. Useful for debugging NIF loading issues. """ @spec nif_function_count() :: non_neg_integer() def nif_function_count do if Code.ensure_loaded?(ExBurn.Nif) do apply(ExBurn.Nif, :debug_nif_count, []) else 0 end rescue _ -> 0 end @doc """ Performs a quick smoke test of the ExBurn pipeline. Creates a small tensor, runs it through the backend, and verifies the result. Returns `:ok` on success or `{:error, reason}` on failure. ## Example case ExBurn.smoke_test() do :ok -> IO.puts("ExBurn is working!") {:error, message} -> IO.puts("ExBurn error: " <> message) end """ @spec smoke_test() :: :ok | {:error, String.t()} def smoke_test do try do Nx.default_backend(ExBurn.Backend) a = Nx.tensor([1.0, 2.0, 3.0]) b = Nx.tensor([4.0, 5.0, 6.0]) result = Nx.add(a, b) [5.0, 7.0, 9.0] = Nx.to_list(result) :ok rescue e -> {:error, "Smoke test failed: #{Exception.message(e)}"} after Nx.default_backend(Nx.BinaryBackend) end end end