# Vignette code is executed locally (NOT_CRAN=true) but not on CRAN, where # the CPU fallback would multi-thread and trip the "CPU time > elapsed" NOTE. knitr::opts_chunk$set(eval = identical(Sys.getenv("NOT_CRAN"), "true"))
There are two ways another package can build on ggmlR:
Imports: ggmlR) and call the
exported ggml_* / ag_* functions. This is the simplest path and is right
for packages that build, train or run models from R. See Part A below.libggml.a) and C
headers (LinkingTo: ggmlR) to call ggml directly from your own C/C++ code.
This is the path used by llamaR (LLM inference) and sd2R (Stable
Diffusion). See Part B below.The two are independent — most packages need only one. You can also combine them (use the R API for orchestration and drop to C for a custom kernel).
In your DESCRIPTION:
Package: myPackage ... Imports: ggmlR
Then import the functions you use in NAMESPACE (via roxygen
@importFrom ggmlR ...), or call them fully qualified as ggmlR::ggml_fit().
#' @importFrom ggmlR ggml_model_sequential ggml_layer_dense ggml_compile #' @importFrom ggmlR ggml_fit ggml_predict NULL
The whole high-level surface is exported and therefore usable from a downstream
package — the sequential, functional and autograd APIs, optimizers, schedulers,
the dataloader, data-parallel training, plus the diagnostics/reproducibility
helpers. For instance, the entire titanic_classification.R example (Keras-like
+ functional + autograd, ~30 ggmlR functions) runs unchanged from another
package once ggmlR is in Imports.
my_train <- function(x, y, epochs = 50L) { model <- ggmlR::ggml_model_sequential() |> ggmlR::ggml_layer_dense(64L, activation = "relu", input_shape = ncol(x)) |> ggmlR::ggml_layer_dense(ncol(y), activation = "softmax") |> ggmlR::ggml_compile(optimizer = "adam", loss = "categorical_crossentropy") ggmlR::ggml_fit(model, x, y, epochs = epochs, verbose = 0L) }
A few things worth knowing:
ggml_compile(), ggml_fit() and
ggml_predict() are S3 generics with methods registered for the sequential
and functional model classes; the dispatch resolves correctly when called
from your package.model$forward(), opt$step(),
dl$epoch(), …). These are fields of objects returned by exported
functions, so they just work — there is nothing extra to import.|> is base R (≥ 4.1); it is not a ggmlR dependency.ggml_set_seed(),
ggml_training_history() and ggml_model_backend() let your package fix
seeds and inspect the loss curve / actual backend of a fitted model.ag_predict_colmajor(), custom autograd layers). Those are
not part of the ggmlR API — copy them into your own package if you want
them.ggmlR registers a parsnip "ggml" engine and mlr3 learners
(classif.ggml, regr.ggml) in its .onLoad: if mlr3 / parsnip are
already loaded they are registered immediately, otherwise a load hook registers
them as soon as those packages load. A downstream package that Imports: ggmlR
therefore gets these registrations with no manual call — just make sure ggmlR's
namespace is loaded (any ggmlR:: call or importFrom does that). See the
tidymodels and mlr3 vignettes for usage.
After installing ggmlR you will find:
$(R_HOME_DIR)/library/ggmlR/lib/libggml.a $(R_HOME_DIR)/library/ggmlR/include/ggml.h $(R_HOME_DIR)/library/ggmlR/include/ggml-backend.h $(R_HOME_DIR)/library/ggmlR/include/ggml-alloc.h $(R_HOME_DIR)/library/ggmlR/include/ggml-opt.h $(R_HOME_DIR)/library/ggmlR/include/ggml-quants.h $(R_HOME_DIR)/library/ggmlR/include/ggml-vulkan.h $(R_HOME_DIR)/library/ggmlR/include/r_ggml_compat.h ... (full list in inst/include/)
r_ggml_compat.h redirects printf/fprintf/abort to R-safe equivalents
(Rprintf, Rf_error). Always include it (or use -include r_ggml_compat.h)
in your C/C++ sources.
Add ggmlR to LinkingTo and Imports:
Package: myPackage ... Imports: ggmlR LinkingTo: ggmlR
LinkingTo makes R add ggmlR/include to the compiler include path
automatically.
src/MakevarsLink against the static library:
GGMLR_LIB = $(shell Rscript -e "cat(system.file('lib', package='ggmlR'))") GGMLR_INC = $(shell Rscript -e "cat(system.file('include', package='ggmlR'))") PKG_CPPFLAGS = -I$(GGMLR_INC) -include r_ggml_compat.h PKG_LIBS = $(GGMLR_LIB)/libggml.a
If ggmlR was built with Vulkan you also need to link Vulkan:
# detect Vulkan (same logic as ggmlR's own configure) VULKAN_LIBS = $(shell pkg-config --libs vulkan 2>/dev/null) PKG_LIBS = $(GGMLR_LIB)/libggml.a $(VULKAN_LIBS)
configure — detect ggmlR at build time#!/bin/sh # configure GGMLR_INC=$(Rscript -e "cat(system.file('include', package='ggmlR'))" 2>/dev/null) GGMLR_LIB=$(Rscript -e "cat(system.file('lib', package='ggmlR'))" 2>/dev/null) if [ -z "$GGMLR_INC" ] || [ ! -f "$GGMLR_LIB/libggml.a" ]; then echo "ERROR: ggmlR not found. Install it first: install.packages('ggmlR')" >&2 exit 1 fi sed -e "s|@GGMLR_INC@|$GGMLR_INC|g" \ -e "s|@GGMLR_LIB@|$GGMLR_LIB|g" \ src/Makevars.in > src/Makevars
src/Makevars.in:
PKG_CPPFLAGS = -I@GGMLR_INC@ -include r_ggml_compat.h PKG_LIBS = @GGMLR_LIB@/libggml.a
/* src/my_model.c */ #include "ggml.h" #include "ggml-backend.h" #include <R.h> #include <Rinternals.h> SEXP R_my_inference(SEXP r_input) { struct ggml_init_params params = { .mem_size = 256 * 1024 * 1024, /* 256 MB */ .mem_buffer = NULL, .no_alloc = false, }; struct ggml_context *ctx = ggml_init(params); int n = length(r_input); struct ggml_tensor *x = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n); memcpy(x->data, REAL(r_input), n * sizeof(float)); /* ... build graph, compute ... */ ggml_free(ctx); return R_NilValue; }
Register in the .Call table (R's standard routine registration):
/* src/init.c */ #include <R.h> #include <Rinternals.h> #include <R_ext/Rdynload.h> extern SEXP R_my_inference(SEXP); static const R_CallMethodDef CallEntries[] = { {"R_my_inference", (DL_FUNC) &R_my_inference, 1}, {NULL, NULL, 0} }; void R_init_myPackage(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); }
my_inference <- function(input) { .Call("R_my_inference", as.numeric(input)) }
ggmlR manages the CPU thread count via ggmlR_get_n_threads() (exported from
r_interface.c). If your package calls ggml_backend_cpu_init() directly,
set the thread count to match:
#include "ggml-backend.h" /* ggmlR_get_n_threads() is exported by ggmlR — link against libggml.a */ extern int ggmlR_get_n_threads(void); ggml_backend_t cpu = ggml_backend_cpu_init(); ggml_backend_cpu_set_n_threads(cpu, ggmlR_get_n_threads());
This ensures your package respects the same thread limit as ggmlR (important for CRAN compliance — tests must not exceed 2 threads).
If your package needs Vulkan, the Vulkan backend is already compiled into
libggml.a when ggmlR was built with Vulkan support. You only need to link
-lvulkan:
VULKAN_LIBS = $(shell pkg-config --libs vulkan 2>/dev/null) PKG_LIBS = @GGMLR_LIB@/libggml.a $(VULKAN_LIBS)
Do not vendor ggml source again — link the pre-built libggml.a from
ggmlR to avoid symbol collisions.
llamaR — LLM inference (LLaMA, Mistral, …) using ggmlR as backendsd2R — Stable Diffusion image generation using ggmlR as backendBoth follow the pattern above: LinkingTo: ggmlR, configure script to
locate headers and libggml.a, thin C wrappers, R .Call interface.
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.