Nothing
# onnx.R — ONNX model inference via ggml backend
#
# Minimal API:
# onnx_load(path, device, input_shapes) — load .onnx file, build ggml graph
# onnx_summary(model) — model metadata
# onnx_run(model, inputs) — run inference
# onnx_inputs(model) — list expected inputs and shapes
#' Load an ONNX model
#'
#' Parses an .onnx file, builds a ggml computation graph, and allocates
#' tensors on the specified device. Weights are loaded via memory-mapped
#' file (zero-copy where possible).
#'
#' @param path Path to .onnx file.
#' @param device Backend device: \code{"vulkan"} (default if available)
#' or \code{"cpu"}.
#' @param input_shapes Optional named list of integer vectors specifying
#' fixed shapes for inputs with dynamic dimensions. Names must match
#' input tensor names. Each shape must include all dimensions including
#' batch, e.g. \code{list(image = c(1L, 3L, 224L, 224L))}.
#' Required when the model has dynamic dimensions and no default shape.
#' @param n_threads Number of CPU threads. \code{NULL} (default) reads
#' \code{getOption("ggmlR.n_threads")}; if that is also unset, uses
#' \code{parallel::detectCores() - 1} (minimum 1).
#' @param dtype Weight precision: \code{"f32"} (default) or \code{"f16"}.
#' When \code{"f16"}, large weight tensors (>= 256 elements) are stored
#' in half-precision for faster Vulkan compute and lower VRAM usage.
#' Small tensors (bias, scalars, batch-norm params) remain in F32
#' for numerical stability. Inputs and outputs are always F32.
#' @return An opaque model object (external pointer) for use with
#' \code{onnx_run()}, \code{onnx_summary()}, and \code{onnx_inputs()}.
#' @export
onnx_load <- function(path, device = NULL, input_shapes = NULL, n_threads = NULL,
dtype = "f32") {
path <- normalizePath(path, mustWork = TRUE)
# Parse the ONNX protobuf
onnx_ptr <- .Call("R_onnx_load", path)
# Get summary before building (onnx_ptr gets consumed by build)
info <- .Call("R_onnx_summary", onnx_ptr)
# Override input shapes if provided
if (!is.null(input_shapes)) {
stopifnot(is.list(input_shapes), !is.null(names(input_shapes)))
shape_names <- names(input_shapes)
shape_vals <- lapply(input_shapes, as.integer)
.Call("R_onnx_override_input_shapes", onnx_ptr, shape_names, shape_vals)
}
# Resolve n_threads: argument > auto (all cores minus 1)
if (is.null(n_threads)) {
nc <- parallel::detectCores(logical = FALSE)
if (is.na(nc)) nc <- 1L
n_threads <- max(nc - 1L, 1L)
}
n_threads <- as.integer(n_threads)
# Validate dtype
dtype <- match.arg(dtype, c("f32", "f16", "fp16", "float16"))
if (dtype %in% c("fp16", "float16")) dtype <- "f16"
# Build ggml graph + allocate on device
ctx_ptr <- .Call("R_onnx_build", onnx_ptr, device, n_threads, dtype)
# Check for remaining dynamic dimensions
inp <- .Call("R_onnx_inputs", ctx_ptr)
for (nm in names(inp)) {
if (any(inp[[nm]] < 0L)) {
dims_str <- paste(ifelse(inp[[nm]] < 0L, "?", inp[[nm]]), collapse = "x")
stop("Input '", nm, "' has dynamic shape [", dims_str, "]. ",
"Specify fixed shape via input_shapes parameter, e.g. ",
"onnx_load(\"", basename(path), "\", input_shapes = list(",
nm, " = c(1, 3, 224, 224))). ",
"Alternatively, re-export the model with static shapes.",
call. = FALSE)
}
}
structure(
list(
ptr = ctx_ptr,
path = path,
ir_version = info$ir_version,
opset = info$opset_version,
producer = info$producer,
graph_name = info$graph_name,
n_nodes = info$n_nodes,
n_weights = info$n_initializers,
ops = info$ops,
dtype = dtype
),
class = "onnx_model"
)
}
#' Print ONNX model summary
#'
#' @param x An \code{onnx_model} object.
#' @param ... Ignored.
#' @return Invisibly returns \code{x}.
#' @export
print.onnx_model <- function(x, ...) {
cat("ONNX Model:", x$graph_name, "\n")
cat(" Producer:", x$producer, "\n")
cat(" IR version:", x$ir_version, "/ Opset:", x$opset, "\n")
cat(" Nodes:", x$n_nodes, "/ Weights:", x$n_weights, "\n")
if (!is.null(x$dtype) && x$dtype != "f32")
cat(" Weight dtype:", toupper(x$dtype), "\n")
cat(" Ops:", paste(x$ops, collapse = ", "), "\n")
invisible(x)
}
#' ONNX model summary
#'
#' Returns metadata about a loaded ONNX model.
#'
#' @param model An \code{onnx_model} object from \code{onnx_load()}.
#' @return A list with \code{ir_version}, \code{opset_version},
#' \code{producer}, \code{graph_name}, \code{n_nodes},
#' \code{n_initializers}, and \code{ops}.
#' @export
onnx_summary <- function(model) {
stopifnot(inherits(model, "onnx_model"))
list(
ir_version = model$ir_version,
opset_version = model$opset,
producer = model$producer,
graph_name = model$graph_name,
n_nodes = model$n_nodes,
n_initializers = model$n_weights,
ops = model$ops
)
}
#' Run ONNX model inference
#'
#' @param model An \code{onnx_model} object from \code{onnx_load()}.
#' @param inputs A named list of numeric vectors/matrices.
#' Names must match the model's input tensor names.
#' Use \code{onnx_inputs()} to see expected names and shapes.
#' @return A named list of output tensors (numeric vectors with dim
#' attributes for multi-dimensional outputs).
#' @export
onnx_run <- function(model, inputs) {
stopifnot(inherits(model, "onnx_model"))
stopifnot(is.list(inputs), !is.null(names(inputs)))
input_names <- names(inputs)
input_data <- lapply(inputs, function(x) as.numeric(x))
.Call("R_onnx_run", model$ptr, input_names, input_data)
}
#' List ONNX model inputs
#'
#' Returns the names and shapes of model inputs (excluding weight
#' initializers). Use this to know what to pass to \code{onnx_run()}.
#'
#' @param model An \code{onnx_model} object from \code{onnx_load()}.
#' @return A named list where names are input tensor names and values
#' are integer vectors of dimension sizes (-1 for dynamic dimensions).
#' @export
onnx_inputs <- function(model) {
stopifnot(inherits(model, "onnx_model"))
.Call("R_onnx_inputs", model$ptr)
}
#' ONNX model device/scheduler diagnostics
#'
#' Returns information about backend placement: which backends are
#' available, how the scheduler splits the graph, and how many ops
#' are supported by GPU vs CPU-only.
#'
#' @param model An \code{onnx_model} object from \code{onnx_load()}.
#' @return A list with:
#' \describe{
#' \item{backends}{Character vector of backend names (e.g. \code{"Vulkan0"}, \code{"CPU"})}
#' \item{n_backends}{Number of backends}
#' \item{n_splits}{Number of scheduler splits (1 = all on one backend)}
#' \item{n_nodes}{Total graph nodes}
#' \item{gpu_ops}{Ops supported by GPU backend}
#' \item{cpu_ops}{Ops that can only run on CPU}
#' \item{cpu_only_ops}{Named integer vector: op type => count (empty if all on GPU)}
#' }
#' @export
onnx_device_info <- function(model) {
stopifnot(inherits(model, "onnx_model"))
.Call("R_onnx_device_info", model$ptr)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.