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().
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.aIf 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/Makevarssrc/Makevars.in:
/* 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);
}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.