---
title: "Using ggmlR as a Backend in Your Package"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{10. Using ggmlR as a Backend in Your Package}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
# 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:

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

```r
#' @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`.

```r
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:

- **S3 dispatch works across packages.** `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.
- **Object methods need no extra imports.** Autograd objects expose methods
  through their environment (`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.
- **The pipe `|>`** is base R (≥ 4.1); it is not a ggmlR dependency.
- **Reproducibility & diagnostics** are exported too: `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.
- **Local helpers stay local.** Example scripts define their own convenience
  helpers (e.g. `ag_predict_colmajor()`, custom autograd layers). Those are
  *not* part of the ggmlR API — copy them into your own package if you want
  them.

## 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:

```makefile
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:

```makefile
# 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

```sh
#!/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`:

```makefile
PKG_CPPFLAGS = -I@GGMLR_INC@ -include r_ggml_compat.h
PKG_LIBS     = @GGMLR_LIB@/libggml.a
```

---

## 5. C code — minimal example

```c
/* 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):

```c
/* 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

```{r}
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:

```c
#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`:

```makefile
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

- `llamaR` — LLM inference (LLaMA, Mistral, …) using ggmlR as backend
- `sd2R` — Stable Diffusion image generation using ggmlR as backend

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