The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Using ggmlR as a Backend in Your Package

There are two ways another package can build on ggmlR:

  1. R level — depend on the ggmlR R API (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.
  2. C level — link against the exported static library (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).


Part A — Using the ggmlR R API from your package

A1. Depend on ggmlR

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

A2. Everything the examples use is exported

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:

A3. tidymodels / mlr3 engines

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.


Part B — Linking ggml from your package’s C/C++ code

1. What ggmlR exports

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.


2. DESCRIPTION

Add ggmlR to LinkingTo and Imports:

Package: myPackage
...
Imports:   ggmlR
LinkingTo: ggmlR

LinkingTo makes R add ggmlR/include to the compiler include path automatically.


3. src/Makevars

Link 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)

4. 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

5. C code — minimal example

/* 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);
}

6. R wrapper

my_inference <- function(input) {
  .Call("R_my_inference", as.numeric(input))
}

7. Thread count

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).


8. Vulkan in downstream packages

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.


9. Real-world references

Both follow the pattern above: LinkingTo: ggmlR, configure script to locate headers and libggml.a, thin C wrappers, R .Call interface.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.