From 08c6d8086cb422d1e09a6a3400c4b4bddc7045de Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 10:52:26 -0500 Subject: [PATCH 01/14] Load the LTX-2.5 transformer through the 2.3 module tree The 2.5 22B DiT is the 2.3 one minus the video feed-forward biases plus a learned keyframe marker. Both flags come from the checkpoint's embedded config via ltx23_dit_config_args(), which the bf16, fp8 and NF4 loaders now use (explicit ... overrides still win). - ltx23_feed_forward/ltx23_transformer_block/ltx23_transformer gain ff_bias, audio_ff_bias and use_keyframes_abs_pos_embedding (video_keyframes_mask in forward, applied after proj_in). - ltx23_open_checkpoint() accepts model_version 2.3* or 2.5*. - The NF4 TorchScript stack takes a zero-length bias as "no bias" and derives the weight rows from absmax and the input width. Verified against the real headers: 2.5 maps 4091/4091 DiT keys and 2.3 4186/4186, none unmapped or unfilled. --- NAMESPACE | 1 + R/checkpoint_ltx23.R | 16 ++-- R/dit_ltx23.R | 62 +++++++++++++- R/dit_ltx23_modules.R | 18 +++-- R/fp8_ltx23.R | 3 +- R/jit_ltx23.R | 21 +++-- R/nf4_ltx23.R | 3 +- R/txt2vid_ltx23.R | 6 +- inst/tinytest/test_jit_ltx23.R | 15 +++- inst/tinytest/test_ltx25_dit.R | 143 +++++++++++++++++++++++++++++++++ man/ltx23_dit_config_args.Rd | 20 +++++ man/ltx23_feed_forward.Rd | 5 +- man/ltx23_open_checkpoint.Rd | 7 +- man/ltx23_transformer.Rd | 17 +++- man/ltx23_transformer_block.Rd | 7 +- 15 files changed, 311 insertions(+), 33 deletions(-) create mode 100644 inst/tinytest/test_ltx25_dit.R create mode 100644 man/ltx23_dit_config_args.Rd diff --git a/NAMESPACE b/NAMESPACE index 3bb9f58..3947d87 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -110,6 +110,7 @@ export(ltx23_census) export(ltx23_connector_transformer_1d) export(ltx23_denormalize_latents) export(ltx23_distilled_sigmas) +export(ltx23_dit_config_args) export(ltx23_downsample1d) export(ltx23_encode_audio) export(ltx23_encode_video_frames) diff --git a/R/checkpoint_ltx23.R b/R/checkpoint_ltx23.R index 59d2619..3c14762 100644 --- a/R/checkpoint_ltx23.R +++ b/R/checkpoint_ltx23.R @@ -18,8 +18,9 @@ NULL #' configuration. #' #' @param path Path to the checkpoint .safetensors file. -#' @param require_version Character. Required \code{model_version} prefix -#' (default "2.3"). Set to NULL to skip the check. +#' @param require_version Character vector. Accepted \code{model_version} +#' prefixes (default \code{c("2.3", "2.5")}, the 22B generations this +#' module tree loads). Set to NULL to skip the check. #' #' @return An object of class \code{ltx23_checkpoint}: a list with #' \code{handle} (safetensors reader), \code{keys}, \code{version}, @@ -32,7 +33,7 @@ NULL #' ckpt <- ltx23_open_checkpoint("ltx-2.3-22b-distilled-1.1.safetensors") #' str(ltx23_split_keys(ckpt$keys), max.level = 1) #' } -ltx23_open_checkpoint <- function(path, require_version = "2.3") { +ltx23_open_checkpoint <- function(path, require_version = c("2.3", "2.5")) { if (!requireNamespace("safetensors", quietly = TRUE)) { stop("The safetensors package is required to read LTX checkpoints.") } @@ -51,14 +52,15 @@ ltx23_open_checkpoint <- function(path, require_version = "2.3") { } if (!is.null(require_version)) { + wanted <- paste0(require_version, "*", collapse = " or ") if (is.null(version)) { stop("Checkpoint has no model_version metadata; expected an LTX ", - require_version, " single-file checkpoint: ", path) + wanted, " single-file checkpoint: ", path) } - if (!startsWith(version, require_version)) { + if (!any(startsWith(version, require_version))) { stop( - "Checkpoint model_version is '", version, "' but '", - require_version, "*' is required: ", path + "Checkpoint model_version is '", version, "' but ", + wanted, " is required: ", path ) } } diff --git a/R/dit_ltx23.R b/R/dit_ltx23.R index 336e0c3..783e9f3 100644 --- a/R/dit_ltx23.R +++ b/R/dit_ltx23.R @@ -36,6 +36,15 @@ NULL #' @param rope_type "split" (LTX-2.3) or "interleaved". #' @param gated_attn,cross_attn_mod,audio_gated_attn,audio_cross_attn_mod,perturbed_attn #' LTX-2.3 feature flags (all TRUE for the 2.3 checkpoints). +#' @param ff_bias,audio_ff_bias Logicals. Feed-forward biases per stream +#' (\code{ff_bias = FALSE} for LTX-2.5). +#' @param use_keyframes_abs_pos_embedding Logical. Carry the LTX-2.5 +#' learned keyframe marker, added after \code{proj_in} to the tokens +#' flagged by the forward's \code{video_keyframes_mask} +#' (\code{[B, S, 1]}, non-zero on keyframe tokens). +#' +#' @seealso \code{\link{ltx23_dit_config_args}} to derive these from a +#' checkpoint's embedded config. #' #' @return Module whose forward(hidden_states, ...) returns #' \code{list(sample, audio_sample)}: the predicted velocity for the @@ -80,7 +89,10 @@ ltx23_transformer <- torch::nn_module( timestep_scale_multiplier = 1000, cross_attn_timestep_scale_multiplier = 1000, rope_type = "split", - perturbed_attn = TRUE + perturbed_attn = TRUE, + ff_bias = TRUE, + audio_ff_bias = TRUE, + use_keyframes_abs_pos_embedding = FALSE ) { inner_dim <- num_attention_heads * attention_head_dim audio_inner_dim <- audio_num_attention_heads * audio_attention_head_dim @@ -94,6 +106,13 @@ ltx23_transformer <- torch::nn_module( self$proj_in <- linear_noinit(in_channels, inner_dim) self$audio_proj_in <- linear_noinit(audio_in_channels, audio_inner_dim) + # LTX-2.5 keyframe marker (zero-initialized in the reference) + if (use_keyframes_abs_pos_embedding) { + self$keyframes_abs_pos_embedding <- torch::nn_parameter( + torch::torch_zeros(1L, inner_dim) + ) + } + # 2. Global timestep modulation (9 params each with cross-attn mod) video_mod_params <- if (cross_attn_mod) 9L else 6L audio_mod_params <- if (audio_cross_attn_mod) 9L else 6L @@ -175,7 +194,9 @@ ltx23_transformer <- torch::nn_module( eps = norm_eps, elementwise_affine = FALSE, rope_type = rope_type, - perturbed_attn = perturbed_attn + perturbed_attn = perturbed_attn, + ff_bias = ff_bias, + audio_ff_bias = audio_ff_bias ) })) @@ -209,7 +230,8 @@ ltx23_transformer <- torch::nn_module( perturbation_mask = NULL, use_cross_timestep = FALSE, video_self_attention_mask = NULL, - cond_token_index = NULL + cond_token_index = NULL, + video_keyframes_mask = NULL ) { audio_timestep <- audio_timestep %||% timestep audio_sigma <- audio_sigma %||% sigma @@ -262,6 +284,15 @@ ltx23_transformer <- torch::nn_module( hidden_states <- self$proj_in(hidden_states) audio_hidden_states <- self$audio_proj_in(audio_hidden_states) + # 2.1. LTX-2.5: mark tokens whose latent frame encodes a single pixel + # frame (causal first frame, generated keyframe slots) + if (!is.null(self$keyframes_abs_pos_embedding) && + !is.null(video_keyframes_mask)) { + marker <- video_keyframes_mask$gt(0)$to(dtype = hidden_states$dtype) + hidden_states <- hidden_states + marker * + self$keyframes_abs_pos_embedding$to(dtype = hidden_states$dtype) + } + # 3. Timestep embeddings and global modulation parameters gate_scale_factor <- self$cross_attn_timestep_scale_multiplier / self$timestep_scale_multiplier @@ -433,6 +464,31 @@ ltx23_transformer <- torch::nn_module( } ) +#' Transformer arguments from a checkpoint's embedded config +#' +#' Reads the version-dependent flags out of the \code{transformer} entry +#' of an LTX single-file checkpoint's embedded config. Every other +#' dimension is shared by the 2.3 and 2.5 22B checkpoints and stays at +#' the \code{\link{ltx23_transformer}} defaults. Keys absent from the +#' config (any 2.3 checkpoint) are left out, so the defaults apply. +#' +#' @param config The parsed embedded config (\code{ckpt$config}), or NULL. +#' +#' @return Named list of \code{\link{ltx23_transformer}} arguments. +#' +#' @export +ltx23_dit_config_args <- function(config) { + tc <- config$transformer + keys <- c("ff_bias", "audio_ff_bias", "use_keyframes_abs_pos_embedding") + args <- list() + for (k in keys) { + if (!is.null(tc[[k]])) { + args[[k]] <- isTRUE(tc[[k]]) + } + } + args +} + #' Map an official DiT checkpoint key to the R module name #' #' Applies the official-to-diffusers renames for the LTX-2.3 transformer diff --git a/R/dit_ltx23_modules.R b/R/dit_ltx23_modules.R index 93288c6..35dff3e 100644 --- a/R/dit_ltx23_modules.R +++ b/R/dit_ltx23_modules.R @@ -491,6 +491,8 @@ ltx23_attention <- torch::nn_module( #' #' @param dim Integer. Input/output dimension. #' @param mult Integer. Hidden dimension multiplier. +#' @param bias Logical. Both linears carry a bias (FALSE for the LTX-2.5 +#' video stream). #' #' @return Module whose forward(x) returns the projected states passed #' through a tanh-approximated GELU, a tensor of the same shape as @@ -499,12 +501,12 @@ ltx23_attention <- torch::nn_module( #' @export ltx23_feed_forward <- torch::nn_module( "ltx23_feed_forward", - initialize = function(dim, mult = 4L) { + initialize = function(dim, mult = 4L, bias = TRUE) { inner_dim <- as.integer(dim * mult) gelu_proj <- torch::nn_module( "ltx23_gelu", initialize = function(dim_in, dim_out) { - self$proj <- linear_noinit(dim_in, dim_out) + self$proj <- linear_noinit(dim_in, dim_out, bias = bias) }, forward = function(x) { h <- self$proj(x) @@ -517,7 +519,7 @@ ltx23_feed_forward <- torch::nn_module( self$net <- torch::nn_module_list(list( gelu_proj(dim, inner_dim), torch::nn_identity(), # dropout slot in the reference; inference no-op - linear_noinit(inner_dim, dim) + linear_noinit(inner_dim, dim, bias = bias) )) }, forward = function(x) { @@ -554,6 +556,8 @@ ltx23_get_mod_params <- function(scale_shift_table, temb, batch_size) { #' @param elementwise_affine Logical. Block norms carry weights (FALSE for LTX). #' @param rope_type "split" or "interleaved". #' @param perturbed_attn Logical. Enable the STG perturbation arguments. +#' @param ff_bias,audio_ff_bias Logicals. Feed-forward biases per stream +#' (LTX-2.5 drops the video ones). #' #' @return Module whose forward(hidden_states, ...) returns #' \code{list(hidden_states, audio_hidden_states)}, the video and audio @@ -579,7 +583,9 @@ ltx23_transformer_block <- torch::nn_module( eps = 1e-6, elementwise_affine = FALSE, rope_type = "split", - perturbed_attn = TRUE + perturbed_attn = TRUE, + ff_bias = TRUE, + audio_ff_bias = TRUE ) { self$perturbed_attn <- perturbed_attn @@ -627,9 +633,9 @@ ltx23_transformer_block <- torch::nn_module( # 4. Feed-forward self$norm3 <- ltx23_rms_norm(dim, eps = eps, elementwise_affine = elementwise_affine) - self$ff <- ltx23_feed_forward(dim) + self$ff <- ltx23_feed_forward(dim, bias = ff_bias) self$audio_norm3 <- ltx23_rms_norm(audio_dim, eps = eps, elementwise_affine = elementwise_affine) - self$audio_ff <- ltx23_feed_forward(audio_dim) + self$audio_ff <- ltx23_feed_forward(audio_dim, bias = audio_ff_bias) # 5. Per-block modulation tables self$video_cross_attn_adaln <- video_cross_attn_adaln diff --git a/R/fp8_ltx23.R b/R/fp8_ltx23.R index 4a7fece..c6cb67b 100644 --- a/R/fp8_ltx23.R +++ b/R/fp8_ltx23.R @@ -287,7 +287,8 @@ ltx23_open_fp8_checkpoint <- function(dir) { ltx23_load_transformer_fp8 <- function(ckpt, device = "cuda", pin = TRUE, verbose = TRUE, ...) { stopifnot(inherits(ckpt, "ltx23_checkpoint")) - model <- .construct_skeleton(ltx23_transformer, ...) + args <- utils::modifyList(ltx23_dit_config_args(ckpt$config), list(...)) + model <- do.call(.construct_skeleton, c(list(ltx23_transformer), args)) groups <- ltx23_split_keys(ckpt$keys) dit_keys <- groups$dit diff --git a/R/jit_ltx23.R b/R/jit_ltx23.R index 27c754f..d6af95c 100644 --- a/R/jit_ltx23.R +++ b/R/jit_ltx23.R @@ -44,10 +44,14 @@ def nf4_lin(x: Tensor, packed: Tensor, absmax: Tensor, bias: Tensor, table2: Ten # per half-byte weight and made dequant the per-step wall. absmax # is cast down before the scale so the product never promotes to # float32 (which would materialize a full-precision weight copy). - rows = bias.size(0) + # A zero-length bias is the no-bias sentinel (List[Tensor] slots + # cannot hold None); the LTX-2.5 video FF has none. vals = torch.embedding(table2, packed.int()) w = vals.reshape([-1, 64]) * absmax.type_as(table2).unsqueeze(1) - return torch.linear(x, w.reshape([rows, -1]).type_as(x), bias) + w = w.reshape([-1, x.size(-1)]).type_as(x) + if bias.numel() == 0: + return torch.linear(x, w) + return torch.linear(x, w, bias) def rmsn(x: Tensor) -> Tensor: v = x.float().pow(2).mean(-1, keepdim=True) @@ -218,9 +222,16 @@ def stack_nf4(h: Tensor, ah: Tensor, enc: Tensor, aenc: Tensor, } .ltx23_jit_pack_ff <- function(ff) { - list(ff$net[[1]]$proj$weight_nf4, ff$net[[1]]$proj$weight_absmax, - ff$net[[1]]$proj$bias, ff$net[[3]]$weight_nf4, - ff$net[[3]]$weight_absmax, ff$net[[3]]$bias) + proj <- ff$net[[1]]$proj + out <- ff$net[[3]] + list(proj$weight_nf4, proj$weight_absmax, .ltx23_jit_bias(proj), + out$weight_nf4, out$weight_absmax, .ltx23_jit_bias(out)) +} + +# A linear's bias, or the zero-length no-bias sentinel nf4_lin expects +.ltx23_jit_bias <- function(lin) { + lin$bias %||% torch::torch_empty(0L, dtype = lin$weight_absmax$dtype, + device = lin$weight_absmax$device) } #' Pack a transformer block's weights for the JIT stack diff --git a/R/nf4_ltx23.R b/R/nf4_ltx23.R index ff47354..a582d4f 100644 --- a/R/nf4_ltx23.R +++ b/R/nf4_ltx23.R @@ -382,7 +382,8 @@ ltx23_quantize_nf4 <- function(checkpoint_path, output_dir = NULL, ltx23_load_transformer_nf4 <- function(ckpt, device = "cuda", verbose = TRUE, ...) { stopifnot(inherits(ckpt, "ltx23_checkpoint")) - model <- .construct_skeleton(ltx23_transformer, ...) + args <- utils::modifyList(ltx23_dit_config_args(ckpt$config), list(...)) + model <- do.call(.construct_skeleton, c(list(ltx23_transformer), args)) groups <- ltx23_split_keys(ckpt$keys) dit_keys <- groups$dit diff --git a/R/txt2vid_ltx23.R b/R/txt2vid_ltx23.R index 7767cc4..0c80eef 100644 --- a/R/txt2vid_ltx23.R +++ b/R/txt2vid_ltx23.R @@ -306,9 +306,11 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", ckpt, device = component_device, pin = pin, verbose = verbose ) } else { + skeleton <- do.call(.construct_skeleton, + c(list(ltx23_transformer, dtype = torch_dtype), + ltx23_dit_config_args(ckpt$config))) pipe$transformer <- load_component( - "dit", .construct_skeleton(ltx23_transformer, dtype = torch_dtype), - ltx23_map_dit_key, transformer_device + "dit", skeleton, ltx23_map_dit_key, transformer_device ) } if (!is.null(attn_chunk)) { diff --git a/inst/tinytest/test_jit_ltx23.R b/inst/tinytest/test_jit_ltx23.R index 78be617..7884291 100644 --- a/inst/tinytest/test_jit_ltx23.R +++ b/inst/tinytest/test_jit_ltx23.R @@ -16,13 +16,13 @@ dim <- 32L; heads <- 2L; head_dim <- 16L; cross_dim <- 32L adim <- 16L; aheads <- 2L; ahead_dim <- 8L; across_dim <- 16L B <- 2L; Sv <- 6L; Sa <- 5L; St <- 4L -make_block <- function() { +make_block <- function(...) { blk <- ltx23_transformer_block( dim = dim, num_attention_heads = heads, attention_head_dim = head_dim, cross_attention_dim = cross_dim, audio_dim = adim, audio_num_attention_heads = aheads, audio_attention_head_dim = ahead_dim, - audio_cross_attention_dim = across_dim + audio_cross_attention_dim = across_dim, ... ) blk$eval() @@ -127,6 +127,17 @@ torch::with_no_grad({ expect_true(max_abs_diff(out2[[1]], ref2[[1]]) < 1e-4) expect_true(max_abs_diff(out2[[2]], ref2[[2]]) < 1e-4) +# LTX-2.5 video FF has no biases: the stack takes the zero-length +# sentinel and must still match eager +blk25 <- make_block(ff_bias = FALSE) +expect_null(blk25$ff$net[[3]]$bias) +torch::with_no_grad({ + ref25 <- eager(blk25, h, ah) + out25 <- jit(list(blk25), h, ah) +}) +expect_true(max_abs_diff(out25[[1]], ref25[[1]]) < 1e-4) +expect_true(max_abs_diff(out25[[2]], ref25[[2]]) < 1e-4) + # The mask must actually mask: moving the -10000 changes the output enc_mask2 <- torch::torch_zeros(B, 1L, St) enc_mask2[, , 1L] <- -10000 diff --git a/inst/tinytest/test_ltx25_dit.R b/inst/tinytest/test_ltx25_dit.R new file mode 100644 index 0000000..ad1d280 --- /dev/null +++ b/inst/tinytest/test_ltx25_dit.R @@ -0,0 +1,143 @@ +# LTX-2.5 transformer deltas on the shared 2.3 module tree: the video +# feed-forward has no biases and a learned keyframe marker joins +# proj_in. Both flags come from the checkpoint's embedded config. + +if (!requireNamespace("torch", quietly = TRUE) || !torch::torch_is_installed()) { + exit_file("torch not fully installed") +} + +library(diffuseR) + +# --- Config flags ---------------------------------------------------------------- + +expect_identical(ltx23_dit_config_args(NULL), list()) +# A 2.3 config carries none of the keys: the defaults apply +expect_identical(ltx23_dit_config_args(list(transformer = list(num_layers = 48L))), + list()) +cfg25 <- list(transformer = list(ff_bias = FALSE, + use_keyframes_abs_pos_embedding = TRUE)) +expect_identical(ltx23_dit_config_args(cfg25), + list(ff_bias = FALSE, use_keyframes_abs_pos_embedding = TRUE)) + +# --- Module shape ---------------------------------------------------------------- + +tiny_cfg <- list( + in_channels = 4L, out_channels = 4L, + num_attention_heads = 2L, attention_head_dim = 8L, + cross_attention_dim = 16L, + audio_in_channels = 4L, audio_out_channels = 4L, + audio_num_attention_heads = 2L, audio_attention_head_dim = 4L, + audio_cross_attention_dim = 8L, + num_layers = 1L +) +torch::torch_manual_seed(3) +m23 <- do.call(ltx23_transformer, tiny_cfg) +m25 <- do.call(ltx23_transformer, c(tiny_cfg, ltx23_dit_config_args(cfg25))) +blk <- m25$transformer_blocks[[1]] +expect_null(blk$ff$net[[1]]$proj$bias) +expect_null(blk$ff$net[[3]]$bias) +expect_false(is.null(blk$audio_ff$net[[1]]$proj$bias)) +expect_null(m23$keyframes_abs_pos_embedding) +expect_equal(as.integer(m25$keyframes_abs_pos_embedding$shape), c(1L, 16L)) +# Exactly the two video FF biases and the marker differ, as in the real +# checkpoints (2 x 48 biases fewer, one marker more) +n23 <- names(m23$named_parameters()) +n25 <- names(m25$named_parameters()) +expect_equal(sort(setdiff(n23, n25)), + sort(c("transformer_blocks.0.ff.net.0.proj.bias", + "transformer_blocks.0.ff.net.2.bias"))) +expect_equal(setdiff(n25, n23), "keyframes_abs_pos_embedding") + +# --- Keyframe marker ------------------------------------------------------------- + +m25$eval() +run <- function(model, ...) { + torch::torch_manual_seed(5) + torch::with_no_grad({ + model( + hidden_states = torch::torch_randn(1L, 24L, 4L), + audio_hidden_states = torch::torch_randn(1L, 5L, 4L), + encoder_hidden_states = torch::torch_randn(1L, 7L, 16L), + audio_encoder_hidden_states = torch::torch_randn(1L, 7L, 8L), + timestep = torch::torch_tensor(700), + sigma = torch::torch_tensor(700), + num_frames = 2L, height = 3L, width = 4L, audio_num_frames = 5L, + use_cross_timestep = TRUE, ... + ) + }) +} +max_abs_diff <- function(a, b) as.numeric((a - b)$abs()$max()) + +torch::with_no_grad(m25$keyframes_abs_pos_embedding$fill_(0.5)) +first_frame <- torch::torch_zeros(1L, 24L, 1L) +first_frame[1, 1:12, 1] <- 1 +base <- run(m25) +expect_true(max_abs_diff(run(m25, video_keyframes_mask = first_frame)$sample, + base$sample) > 1e-4) +# An all-zero mask marks nothing +expect_true(max_abs_diff(run(m25, video_keyframes_mask = torch::torch_zeros(1L, 24L, 1L))$sample, + base$sample) < 1e-7) + +# --- 2.5 checkpoint through the NF4 and fp8 artifacts --------------------------- + +if (!requireNamespace("safetensors", quietly = TRUE)) { + exit_file("safetensors not installed") +} + +to_official <- function(name) { + name <- gsub("av_cross_attn_video_scale_shift", "av_ca_video_scale_shift_adaln_single", name, fixed = TRUE) + name <- gsub("av_cross_attn_video_a2v_gate", "av_ca_a2v_gate_adaln_single", name, fixed = TRUE) + name <- gsub("av_cross_attn_audio_scale_shift", "av_ca_audio_scale_shift_adaln_single", name, fixed = TRUE) + name <- gsub("av_cross_attn_audio_v2a_gate", "av_ca_v2a_gate_adaln_single", name, fixed = TRUE) + name <- gsub("video_a2v_cross_attn_scale_shift_table", "scale_shift_table_a2v_ca_video", name, fixed = TRUE) + name <- gsub("audio_a2v_cross_attn_scale_shift_table", "scale_shift_table_a2v_ca_audio", name, fixed = TRUE) + name <- gsub("prompt_adaln", "prompt_adaln_single", name, fixed = TRUE) + name <- sub("^audio_time_embed\\.", "audio_adaln_single.", name) + name <- sub("^time_embed\\.", "adaln_single.", name) + name <- gsub("proj_in", "patchify_proj", name, fixed = TRUE) + name <- gsub("norm_q", "q_norm", name, fixed = TRUE) + name <- gsub("norm_k", "k_norm", name, fixed = TRUE) + paste0("model.diffusion_model.", name) +} + +params <- m25$named_parameters() +tensors <- list() +for (name in names(params)) { + tensors[[to_official(name)]] <- params[[name]]$detach()$clone() +} +src <- tempfile(fileext = ".safetensors") +on.exit(unlink(src), add = TRUE) +safetensors::safe_save_file(tensors, src, metadata = list( + model_version = "2.5.0", + config = as.character(jsonlite::toJSON(cfg25, auto_unbox = TRUE)) +)) + +# The single-file opener accepts 2.5 and parses the flags back out +ckpt25 <- ltx23_open_checkpoint(src) +expect_equal(ckpt25$version, "2.5.0") +expect_identical(ltx23_dit_config_args(ckpt25$config), + list(ff_bias = FALSE, use_keyframes_abs_pos_embedding = TRUE)) + +for (fmt in c("nf4", "fp8")) { + dir <- tempfile(paste0(fmt, "_")) + quantize <- if (fmt == "nf4") ltx23_quantize_nf4 else ltx23_quantize_fp8 + load <- if (fmt == "nf4") ltx23_load_transformer_nf4 else ltx23_load_transformer_fp8 + manifest <- quantize(src, dir, verbose = FALSE) + expect_equal(manifest[[paste0(fmt, "_cast")]], + sum(ltx23_is_fp8_cast_key(names(params))), info = fmt) + + # No ff_bias argument here: it must arrive through the manifest config. + # An incomplete load (a missing or surplus bias) stops inside the loader. + q <- do.call(load, c(list(ckpt = ltx23_open_fp8_checkpoint(dir), + device = "cpu", verbose = FALSE), tiny_cfg)) + qblk <- q$transformer_blocks[[1]] + expect_null(qblk$ff$net[[3]]$bias, info = fmt) + expect_false(is.null(qblk$audio_ff$net[[3]]$bias), info = fmt) + expect_equal(as.numeric(q$keyframes_abs_pos_embedding$float()$mean()), 0.5, + info = fmt) + + q$to(dtype = torch::torch_float32()) + out <- run(q, video_keyframes_mask = first_frame) + expect_true(as.logical(out$sample$isfinite()$all()$item()), info = fmt) + unlink(dir, recursive = TRUE) +} diff --git a/man/ltx23_dit_config_args.Rd b/man/ltx23_dit_config_args.Rd new file mode 100644 index 0000000..ac2f874 --- /dev/null +++ b/man/ltx23_dit_config_args.Rd @@ -0,0 +1,20 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{ltx23_dit_config_args} +\alias{ltx23_dit_config_args} +\title{Transformer arguments from a checkpoint's embedded config} +\usage{ +ltx23_dit_config_args(config) +} +\arguments{ +\item{config}{The parsed embedded config (\code{ckpt$config}), or NULL.} +} +\value{ +Named list of \code{\link{ltx23_transformer}} arguments. +} +\description{ +Reads the version-dependent flags out of the \code{transformer} entry +of an LTX single-file checkpoint's embedded config. Every other +dimension is shared by the 2.3 and 2.5 22B checkpoints and stays at +the \code{\link{ltx23_transformer}} defaults. Keys absent from the +config (any 2.3 checkpoint) are left out, so the defaults apply. +} diff --git a/man/ltx23_feed_forward.Rd b/man/ltx23_feed_forward.Rd index aaa197f..b99c329 100644 --- a/man/ltx23_feed_forward.Rd +++ b/man/ltx23_feed_forward.Rd @@ -3,12 +3,15 @@ \alias{ltx23_feed_forward} \title{LTX feed-forward layer} \usage{ -ltx23_feed_forward(dim, mult = 4L) +ltx23_feed_forward(dim, mult = 4L, bias = TRUE) } \arguments{ \item{dim}{Integer. Input/output dimension.} \item{mult}{Integer. Hidden dimension multiplier.} + +\item{bias}{Logical. Both linears carry a bias (FALSE for the LTX-2.5 +video stream).} } \value{ Module whose forward(x) returns the projected states passed diff --git a/man/ltx23_open_checkpoint.Rd b/man/ltx23_open_checkpoint.Rd index 980ada2..80d0abc 100644 --- a/man/ltx23_open_checkpoint.Rd +++ b/man/ltx23_open_checkpoint.Rd @@ -3,13 +3,14 @@ \alias{ltx23_open_checkpoint} \title{Open an LTX-2.3 checkpoint} \usage{ -ltx23_open_checkpoint(path, require_version = "2.3") +ltx23_open_checkpoint(path, require_version = c("2.3", "2.5")) } \arguments{ \item{path}{Path to the checkpoint .safetensors file.} -\item{require_version}{Character. Required \code{model_version} prefix -(default "2.3"). Set to NULL to skip the check.} +\item{require_version}{Character vector. Accepted \code{model_version} +prefixes (default \code{c("2.3", "2.5")}, the 22B generations this +module tree loads). Set to NULL to skip the check.} } \value{ An object of class \code{ltx23_checkpoint}: a list with diff --git a/man/ltx23_transformer.Rd b/man/ltx23_transformer.Rd index 4e2cf25..717abb1 100644 --- a/man/ltx23_transformer.Rd +++ b/man/ltx23_transformer.Rd @@ -38,7 +38,10 @@ ltx23_transformer( timestep_scale_multiplier = 1000, cross_attn_timestep_scale_multiplier = 1000, rope_type = "split", - perturbed_attn = TRUE + perturbed_attn = TRUE, + ff_bias = TRUE, + audio_ff_bias = TRUE, + use_keyframes_abs_pos_embedding = FALSE ) } \arguments{ @@ -54,6 +57,11 @@ ltx23_transformer( \item{rope_type}{"split" (LTX-2.3) or "interleaved".} +\item{use_keyframes_abs_pos_embedding}{Logical. Carry the LTX-2.5 +learned keyframe marker, added after \code{proj_in} to the tokens +flagged by the forward's \code{video_keyframes_mask} +(\code{[B, S, 1]}, non-zero on keyframe tokens).} + \item{in_channels,out_channels}{Integers. Video latent channels.} \item{patch_size,patch_size_t}{Integers. Video patch sizes.} @@ -76,6 +84,9 @@ ltx23_transformer( the a2v/v2a gates).} \item{gated_attn,cross_attn_mod,audio_gated_attn,audio_cross_attn_mod,perturbed_attn}{LTX-2.3 feature flags (all TRUE for the 2.3 checkpoints).} + +\item{ff_bias,audio_ff_bias}{Logicals. Feed-forward biases per stream +(\code{ff_bias = FALSE} for LTX-2.5).} } \value{ Module whose forward(hidden_states, ...) returns @@ -88,3 +99,7 @@ Dual-stream audio/video DiT. Text embeddings arrive already projected to the video (\code{inner_dim}) and audio (\code{audio_inner_dim}) dimensions by the connector modules. } +\seealso{ +\code{\link{ltx23_dit_config_args}} to derive these from a + checkpoint's embedded config. +} diff --git a/man/ltx23_transformer_block.Rd b/man/ltx23_transformer_block.Rd index ae190aa..32adb7a 100644 --- a/man/ltx23_transformer_block.Rd +++ b/man/ltx23_transformer_block.Rd @@ -19,7 +19,9 @@ ltx23_transformer_block( eps = 1e-06, elementwise_affine = FALSE, rope_type = "split", - perturbed_attn = TRUE + perturbed_attn = TRUE, + ff_bias = TRUE, + audio_ff_bias = TRUE ) } \arguments{ @@ -45,6 +47,9 @@ ltx23_transformer_block( \item{video_cross_attn_adaln,audio_cross_attn_adaln}{Logicals. LTX-2.3 text cross-attention modulation (9 mod params instead of 6).} + +\item{ff_bias,audio_ff_bias}{Logicals. Feed-forward biases per stream +(LTX-2.5 drops the video ones).} } \value{ Module whose forward(hidden_states, ...) returns From 549735079b16ff11856a4d573c8cd4d20b1a85d7 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 11:24:44 -0500 Subject: [PATCH 02/14] Test the dense LTX transformer load; scope 2.5 test fixtures Review follow-ups to 08c6d80: - The unquantized transformer load moves out of ltx23_load_pipeline() into .ltx23_load_transformer(), which takes ... like the fp8/NF4 loaders, so a tiny config can exercise it. The test checks the 2.5 flags arrive through the embedded config and every parameter round trips exactly at float32. - Fixture files and quantization directories are created and removed inside local() scopes; the test asserts the source checkpoint is gone. --- R/dit_ltx23.R | 24 ++++++++ R/txt2vid_ltx23.R | 8 +-- inst/tinytest/test_ltx25_dit.R | 101 ++++++++++++++++++++------------- 3 files changed, 89 insertions(+), 44 deletions(-) diff --git a/R/dit_ltx23.R b/R/dit_ltx23.R index 783e9f3..fad921d 100644 --- a/R/dit_ltx23.R +++ b/R/dit_ltx23.R @@ -489,6 +489,30 @@ ltx23_dit_config_args <- function(config) { args } +# Load the unquantized transformer from a single-file checkpoint: a +# skeleton built from the embedded config (explicit ... wins, as in the +# fp8/NF4 loaders), streamed in at `dtype`, refused unless every key +# lands and every parameter is filled. +.ltx23_load_transformer <- function(ckpt, dtype, device = "cpu", + verbose = TRUE, ...) { + args <- utils::modifyList(ltx23_dit_config_args(ckpt$config), list(...)) + model <- do.call(.construct_skeleton, + c(list(ltx23_transformer, dtype = dtype), args)) + keys <- ltx23_split_keys(ckpt$keys)$dit + if (verbose) { + message("Loading dit (", length(keys), " tensors)...") + } + res <- ltx23_load_group(ckpt, keys, model, map_key = ltx23_map_dit_key, + verbose = verbose) + if (length(res$unmapped) || length(res$unfilled)) { + stop("dit: incomplete load (", length(res$unmapped), " unmapped, ", + length(res$unfilled), " unfilled)") + } + model$to(device = device) + model$eval() + model +} + #' Map an official DiT checkpoint key to the R module name #' #' Applies the official-to-diffusers renames for the LTX-2.3 transformer diff --git a/R/txt2vid_ltx23.R b/R/txt2vid_ltx23.R index 0c80eef..8815a3c 100644 --- a/R/txt2vid_ltx23.R +++ b/R/txt2vid_ltx23.R @@ -306,11 +306,9 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", ckpt, device = component_device, pin = pin, verbose = verbose ) } else { - skeleton <- do.call(.construct_skeleton, - c(list(ltx23_transformer, dtype = torch_dtype), - ltx23_dit_config_args(ckpt$config))) - pipe$transformer <- load_component( - "dit", skeleton, ltx23_map_dit_key, transformer_device + pipe$transformer <- .ltx23_load_transformer( + ckpt, dtype = torch_dtype, device = transformer_device, + verbose = verbose ) } if (!is.null(attn_chunk)) { diff --git a/inst/tinytest/test_ltx25_dit.R b/inst/tinytest/test_ltx25_dit.R index ad1d280..0b0d4b4 100644 --- a/inst/tinytest/test_ltx25_dit.R +++ b/inst/tinytest/test_ltx25_dit.R @@ -101,43 +101,66 @@ to_official <- function(name) { } params <- m25$named_parameters() -tensors <- list() -for (name in names(params)) { - tensors[[to_official(name)]] <- params[[name]]$detach()$clone() -} -src <- tempfile(fileext = ".safetensors") -on.exit(unlink(src), add = TRUE) -safetensors::safe_save_file(tensors, src, metadata = list( - model_version = "2.5.0", - config = as.character(jsonlite::toJSON(cfg25, auto_unbox = TRUE)) -)) - -# The single-file opener accepts 2.5 and parses the flags back out -ckpt25 <- ltx23_open_checkpoint(src) -expect_equal(ckpt25$version, "2.5.0") -expect_identical(ltx23_dit_config_args(ckpt25$config), - list(ff_bias = FALSE, use_keyframes_abs_pos_embedding = TRUE)) -for (fmt in c("nf4", "fp8")) { - dir <- tempfile(paste0(fmt, "_")) - quantize <- if (fmt == "nf4") ltx23_quantize_nf4 else ltx23_quantize_fp8 - load <- if (fmt == "nf4") ltx23_load_transformer_nf4 else ltx23_load_transformer_fp8 - manifest <- quantize(src, dir, verbose = FALSE) - expect_equal(manifest[[paste0(fmt, "_cast")]], - sum(ltx23_is_fp8_cast_key(names(params))), info = fmt) - - # No ff_bias argument here: it must arrive through the manifest config. - # An incomplete load (a missing or surplus bias) stops inside the loader. - q <- do.call(load, c(list(ckpt = ltx23_open_fp8_checkpoint(dir), - device = "cpu", verbose = FALSE), tiny_cfg)) - qblk <- q$transformer_blocks[[1]] - expect_null(qblk$ff$net[[3]]$bias, info = fmt) - expect_false(is.null(qblk$audio_ff$net[[3]]$bias), info = fmt) - expect_equal(as.numeric(q$keyframes_abs_pos_embedding$float()$mean()), 0.5, - info = fmt) - - q$to(dtype = torch::torch_float32()) - out <- run(q, video_keyframes_mask = first_frame) - expect_true(as.logical(out$sample$isfinite()$all()$item()), info = fmt) - unlink(dir, recursive = TRUE) -} +# Every temp file and directory is created and removed inside its own +# local() scope, so a failing expectation or loader error still cleans up +src_seen <- local({ + tensors <- list() + for (name in names(params)) { + tensors[[to_official(name)]] <- params[[name]]$detach()$clone() + } + src <- tempfile(fileext = ".safetensors") + on.exit(unlink(src), add = TRUE) + safetensors::safe_save_file(tensors, src, metadata = list( + model_version = "2.5.0", + config = as.character(jsonlite::toJSON(cfg25, auto_unbox = TRUE)) + )) + + # The single-file opener accepts 2.5 and parses the flags back out + ckpt25 <- ltx23_open_checkpoint(src) + expect_equal(ckpt25$version, "2.5.0") + expect_identical(ltx23_dit_config_args(ckpt25$config), + list(ff_bias = FALSE, use_keyframes_abs_pos_embedding = TRUE)) + + # Dense (bf16/fp32) loader, the ltx23_load_pipeline() path. No ff_bias + # argument: it must arrive through the embedded config, and an + # incomplete load stops inside the loader. Loading at float32 makes + # the round trip exact. + d <- do.call(diffuseR:::.ltx23_load_transformer, + c(list(ckpt = ckpt25, dtype = torch::torch_float32(), + verbose = FALSE), tiny_cfg)) + expect_null(d$transformer_blocks[[1]]$ff$net[[3]]$bias) + loaded <- d$named_parameters() + expect_equal(sort(names(loaded)), sort(names(params))) + same <- vapply(names(params), function(n) { + as.logical(torch::torch_equal(loaded[[n]], params[[n]])) + }, logical(1)) + expect_true(all(same)) + + for (fmt in c("nf4", "fp8")) local({ + dir <- tempfile(paste0(fmt, "_")) + on.exit(unlink(dir, recursive = TRUE), add = TRUE) + quantize <- if (fmt == "nf4") ltx23_quantize_nf4 else ltx23_quantize_fp8 + load <- if (fmt == "nf4") ltx23_load_transformer_nf4 else ltx23_load_transformer_fp8 + manifest <- quantize(src, dir, verbose = FALSE) + expect_equal(manifest[[paste0(fmt, "_cast")]], + sum(ltx23_is_fp8_cast_key(names(params))), info = fmt) + + # The flags reach the quantized loaders through the manifest config + q <- do.call(load, c(list(ckpt = ltx23_open_fp8_checkpoint(dir), + device = "cpu", verbose = FALSE), tiny_cfg)) + qblk <- q$transformer_blocks[[1]] + expect_null(qblk$ff$net[[3]]$bias, info = fmt) + expect_false(is.null(qblk$audio_ff$net[[3]]$bias), info = fmt) + expect_equal(as.numeric(q$keyframes_abs_pos_embedding$float()$mean()), 0.5, + info = fmt) + + q$to(dtype = torch::torch_float32()) + out <- run(q, video_keyframes_mask = first_frame) + expect_true(as.logical(out$sample$isfinite()$all()$item()), info = fmt) + }) + src +}) + +# The scopes above removed their fixtures +expect_false(file.exists(src_seen)) From 3cb74f07b4d35b6b9f0a6534660f491fcf290383 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 11:30:13 -0500 Subject: [PATCH 03/14] Sample LTX-2.5 with the ancestral Euler step LTX-2.5 and later are distilled for the rectified-flow ancestral sampler (eta 1); 2.3 keeps plain Euler. txt2vid_ltx2() gains sampler = "auto" (chosen from the checkpoint's model_version, now kept on the pipeline), "euler" or "euler_ancestral", added last so positional calls are unaffected. - .ltx23_step(): the diffusers LTXEulerAncestralRFScheduler step; eta 0 is exactly the previous x + dt * v. - .ltx23_denoise() draws per-step noise (video, then audio) from seed + 10000 (stage 1) / seed + 20000 (stage 2) and restores the caller's RNG state, so the initial and stage-2 entry noise match the Euler run at the same seed. - Tests: Euler identity, last-step denoise, the marginal property (exact velocity at sigma steps to (1 - s_next) x0 + N(0, s_next^2)), version selection, and seeded/restoring loop behavior on a tiny DiT. - REFERENCES.md records sources for both 2.5 changes so far. --- R/txt2vid_ltx23.R | 87 ++++++++++++++++++++++---- inst/REFERENCES.md | 2 + inst/tinytest/test_ltx25_sampler.R | 98 ++++++++++++++++++++++++++++++ man/txt2vid_ltx2.Rd | 10 ++- 4 files changed, 185 insertions(+), 12 deletions(-) create mode 100644 inst/tinytest/test_ltx25_sampler.R diff --git a/R/txt2vid_ltx23.R b/R/txt2vid_ltx23.R index 8815a3c..e1f17c8 100644 --- a/R/txt2vid_ltx23.R +++ b/R/txt2vid_ltx23.R @@ -77,18 +77,66 @@ ltx23_unpack_audio_latents <- function(latents, num_mel_bins) { latents * std + mean } -# Joint audio/video Euler denoise loop over a sigma schedule (CFG-free). +# Sampler eta for a pipeline: LTX-2.5 and later are distilled for the +# ancestral sampler (eta 1), LTX-2.3 for plain Euler (eta 0). "auto" +# with no recorded version keeps the 2.3 behavior. +.ltx23_sampler_eta <- function(sampler, model_version) { + if (sampler == "auto") { + v <- numeric_version(model_version %||% "0", strict = FALSE) + ancestral <- !is.na(v) && v >= "2.5" + sampler <- if (ancestral) "euler_ancestral" else "euler" + } + if (sampler == "euler_ancestral") 1 else 0 +} + +# One rectified-flow sampler step from sigma to sigma_next, in float32 +# (diffusers LTXEulerAncestralRFScheduler.step, Apache-2.0). eta = 0 is +# the plain Euler step x + (sigma_next - sigma) * v. eta > 0 steps +# deterministically to sigma_down <= sigma_next, then renoises back up +# to sigma_next with the signal rescaled by alpha_next / alpha_down +# (alpha = 1 - sigma), which keeps the transition variance-preserving. +# The last step (sigma_next = 0) returns the denoised estimate. +.ltx23_step <- function(x, velocity, sigma, sigma_next, eta = 0, + noise = NULL, s_noise = 1) { + if (eta == 0) { + return(x + velocity * (sigma_next - sigma)) + } + denoised <- x - velocity * sigma + if (abs(sigma_next) < 1e-8) { + return(denoised) + } + sigma_down <- sigma_next * (1 + (sigma_next / sigma - 1) * eta) + alpha_next <- 1 - sigma_next + alpha_down <- 1 - sigma_down + ratio <- sigma_down / sigma + x <- x * ratio + denoised * (1 - ratio) + renoise <- sqrt(max(sigma_next^2 - + sigma_down^2 * alpha_next^2 / (alpha_down^2 + 1e-12), 0)) + x * (alpha_next / (alpha_down + 1e-12)) + noise * (renoise * s_noise) +} + +# Joint audio/video denoise loop over a sigma schedule (CFG-free). # With a conditioning_mask [B, S] (prefix conditioning), conditioned # video tokens see a per-token timestep of zero and are frozen through -# the Euler updates (reference i2v semantics at strength 1). +# the updates (reference i2v semantics at strength 1). eta > 0 selects +# the ancestral step (LTX-2.5); its per-step noise (video, then audio) +# comes from noise_seed when given, with the caller's RNG state restored +# afterwards so later draws do not depend on the step count. .ltx23_denoise <- function(transformer, latents, audio_latents, sigmas, video_text_embeds, audio_text_embeds, text_mask, latent_frames, latent_height, latent_width, audio_num_frames, frame_rate, device, compute_dtype, verbose = TRUE, stage = "", conditioning_mask = NULL, - audio_conditioned = FALSE) { + audio_conditioned = FALSE, eta = 0, + noise_seed = NULL) { f32 <- torch::torch_float32() + if (eta > 0 && !is.null(noise_seed)) { + torch::local_torch_manual_seed(noise_seed) + } + step_noise <- function(like) { + if (eta > 0) torch::torch_randn_like(like) else NULL + } keep_mask <- if (!is.null(conditioning_mask)) { conditioning_mask$unsqueeze(3L) # [B, S, 1] for latent blending } else { @@ -161,9 +209,10 @@ ltx23_unpack_audio_latents <- function(latents, num_mel_bins) { cond_token_index = cond_idx ) - # Euler velocity step in float32; dt is negative (sigma decreasing) - dt <- torch::torch_tensor(sigmas[i + 1L] - sigma, device = device, dtype = f32) - stepped <- latents + dt * out$sample$to(dtype = f32) + # Sampler step in float32 (latents are float32 throughout) + sigma_next <- sigmas[i + 1L] + stepped <- .ltx23_step(latents, out$sample$to(dtype = f32), sigma, + sigma_next, eta, step_noise(latents)) latents <- if (is.null(keep_mask)) { stepped } else { @@ -171,7 +220,10 @@ ltx23_unpack_audio_latents <- function(latents, num_mel_bins) { latents * keep_mask + stepped * (1 - keep_mask) } if (!audio_conditioned) { - audio_latents <- audio_latents + dt * out$audio_sample$to(dtype = f32) + audio_latents <- .ltx23_step(audio_latents, + out$audio_sample$to(dtype = f32), + sigma, sigma_next, eta, + step_noise(audio_latents)) } rm(out) gc(verbose = FALSE) @@ -278,7 +330,8 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", float32 = torch::torch_float32(), stop("Unsupported dtype: ", dtype)) - pipe <- list(config = ckpt$config, checkpoint_path = ckpt$path) + pipe <- list(config = ckpt$config, checkpoint_path = ckpt$path, + model_version = ckpt$version) load_component <- function(name, module, map_key, dev) { if (verbose) { @@ -433,6 +486,12 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", #' @param guidance_scale Numeric. Only 1 (no CFG) is supported; the #' distilled checkpoints are trained for CFG-free sampling. #' @param seed Integer or NULL. +#' @param sampler One of "auto", "euler" or "euler_ancestral". "auto" +#' picks the sampler the checkpoint was distilled for: plain Euler +#' for LTX-2.3, ancestral Euler (eta 1) for LTX-2.5 and later. The +#' ancestral steps draw their noise from \code{seed + 10000} (stage 1) +#' and \code{seed + 20000} (stage 2), so the initial noise matches the +#' Euler run at the same seed. #' @param device Character. Compute device for the denoising loop. #' @param dtype Character. Model compute dtype ("bfloat16" or "float32"). #' @param filename Character or NULL. Output video path (.mp4). Audio is @@ -517,7 +576,8 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, image = NULL, condition_video = NULL, conditioning_frames = 9L, cond_noise_scale = 0, condition_latents = NULL, resident = character(), - trim_frames = 0L, audio = NULL, verbose = TRUE) { + trim_frames = 0L, audio = NULL, verbose = TRUE, + sampler = c("auto", "euler", "euler_ancestral")) { level <- .verbosity(verbose) verbose <- level == "steps" if (level != "silent") { @@ -526,6 +586,7 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, if (two_stage) " (two-stage)" else "")) } stopifnot(inherits(pipeline, "ltx23_pipeline")) + eta <- .ltx23_sampler_eta(match.arg(sampler), pipeline$model_version) if (guidance_scale != 1) { stop("Only guidance_scale = 1 is supported (distilled checkpoints).") } @@ -812,7 +873,9 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, device, compute_dtype, verbose = level, stage = if (two_stage) "stage 1 " else "", conditioning_mask = conditioning_mask, - audio_conditioned = audio_conditioned + audio_conditioned = audio_conditioned, + eta = eta, + noise_seed = if (!is.null(seed)) seed + 10000 ) latents <- denoised$latents audio_latents <- denoised$audio_latents @@ -868,7 +931,9 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, video_text_embeds, audio_text_embeds, text_mask, latent_frames, latent_height, latent_width, audio_num_frames, frame_rate, - device, compute_dtype, verbose = level, stage = "stage 2 " + device, compute_dtype, verbose = level, stage = "stage 2 ", + eta = eta, + noise_seed = if (!is.null(seed)) seed + 20000 ) latents <- denoised$latents audio_latents <- denoised$audio_latents diff --git a/inst/REFERENCES.md b/inst/REFERENCES.md index fb53187..9d71b12 100644 --- a/inst/REFERENCES.md +++ b/inst/REFERENCES.md @@ -14,6 +14,8 @@ module; this file documents the actual lineage, idea by idea. | Distilled sigma schedules, default negative prompt | diffusers `pipelines/ltx2/utils.py` (Apache-2.0); values originate from Lightricks' LTX-2 release (numeric facts) | | Single-file checkpoint key layout, embedded config, `model_version` metadata | Format facts of the official Lightricks checkpoints, cross-checked against diffusers `scripts/convert_ltx2_to_diffusers.py` (Apache-2.0) | | Gemma3 text encoder | Ported from HuggingFace **transformers** (Apache-2.0) | +| LTX-2.5 transformer flags (`ff_bias = FALSE` video FFN, `keyframes_abs_pos_embedding` after `proj_in`) | diffusers `models/transformers/transformer_ltx2.py` (Apache-2.0); flag values from the embedded config of the official LTX-2.5 checkpoints (format facts) | +| Ancestral (SDE) Euler step for LTX-2.5, eta 1 | Ported from diffusers `schedulers/scheduling_ltx_euler_ancestral_rf.py` (Apache-2.0). That LTX-2.5+ checkpoints use it, and the stage noise seeds `seed + 10000` / `seed + 20000`, are numeric facts from Lightricks' LTX-2 release (`ANCESTRAL_SAMPLER_SINCE_VERSION`, LTX-2 Community License; no code taken) | ## FLUX.1-schnell diff --git a/inst/tinytest/test_ltx25_sampler.R b/inst/tinytest/test_ltx25_sampler.R new file mode 100644 index 0000000..fdbcf51 --- /dev/null +++ b/inst/tinytest/test_ltx25_sampler.R @@ -0,0 +1,98 @@ +# The LTX sampler step: plain Euler for 2.3, rectified-flow ancestral +# Euler (eta 1) for 2.5, and the loop's seeded, state-restoring noise. + +if (!requireNamespace("torch", quietly = TRUE) || !torch::torch_is_installed()) { + exit_file("torch not fully installed") +} + +library(diffuseR) +step <- diffuseR:::.ltx23_step +max_abs_diff <- function(a, b) as.numeric((a - b)$abs()$max()) + +torch::torch_manual_seed(11) +x <- torch::torch_randn(2L, 7L, 4L) +v <- torch::torch_randn(2L, 7L, 4L) + +# eta = 0 is the 2.3 Euler step, whatever noise is passed +expect_true(max_abs_diff(step(x, v, 0.9, 0.725), x + v * (0.725 - 0.9)) < 1e-7) +expect_true(max_abs_diff(step(x, v, 0.9, 0.725, eta = 0, noise = torch::torch_randn(2L, 7L, 4L)), + x + v * (0.725 - 0.9)) < 1e-7) +# The last ancestral step lands on the denoised estimate x - sigma * v +expect_true(max_abs_diff(step(x, v, 0.421875, 0, eta = 1, noise = x), x - v * 0.421875) < 1e-6) + +# Marginal check: x_t = (1 - s) x0 + s eps with the exact velocity +# v = eps - x0 must step to (1 - s_next) x0 + N(0, s_next^2) +local({ + torch::torch_manual_seed(12) + n <- 400000L + x0 <- 0.8 + for (pair in list(c(1.0, 0.99375), c(0.909375, 0.725), c(0.725, 0.421875))) { + s <- pair[1]; sn <- pair[2] + eps <- torch::torch_randn(n) + xt <- eps * s + x0 * (1 - s) + out <- step(xt, eps - x0, s, sn, eta = 1, noise = torch::torch_randn(n)) + expect_equal(out$mean()$item(), (1 - sn) * x0, tolerance = 0.01, info = s) + expect_equal(out$std()$item(), sn, tolerance = 0.01, info = s) + } + # Euler on the same input keeps the original noise draw scaled to s_next + eps <- torch::torch_randn(n) + xt <- eps * 0.9 + x0 * 0.1 + expect_true(max_abs_diff(step(xt, eps - x0, 0.9, 0.725), + eps * 0.725 + x0 * 0.275) < 1e-5) +}) + +# --- Sampler selection ----------------------------------------------------------- + +eta_for <- diffuseR:::.ltx23_sampler_eta +expect_equal(eta_for("auto", "2.3.0"), 0) +expect_equal(eta_for("auto", "2.5.0"), 1) +expect_equal(eta_for("auto", "2.10.1"), 1) +expect_equal(eta_for("auto", NULL), 0) +expect_equal(eta_for("euler", "2.5.0"), 0) +expect_equal(eta_for("euler_ancestral", "2.3.0"), 1) + +# --- The denoise loop on a tiny transformer ------------------------------------ + +model <- ltx23_transformer( + in_channels = 4L, out_channels = 4L, + num_attention_heads = 2L, attention_head_dim = 8L, + cross_attention_dim = 16L, + audio_in_channels = 4L, audio_out_channels = 4L, + audio_num_attention_heads = 2L, audio_attention_head_dim = 4L, + audio_cross_attention_dim = 8L, + num_layers = 1L +) +model$eval() +torch::torch_manual_seed(13) +lat <- torch::torch_randn(1L, 24L, 4L) +alat <- torch::torch_randn(1L, 5L, 4L) +venc <- torch::torch_randn(1L, 6L, 16L) +aenc <- torch::torch_randn(1L, 6L, 8L) +tmask <- torch::torch_ones(1L, 6L) +denoise <- function(eta, noise_seed = NULL) { + diffuseR:::.ltx23_denoise( + model, lat, alat, c(1.0, 0.909375, 0.725, 0.0), + venc, aenc, tmask, + latent_frames = 2L, latent_height = 3L, latent_width = 4L, + audio_num_frames = 5L, frame_rate = 24, device = "cpu", + compute_dtype = torch::torch_float32(), verbose = FALSE, + eta = eta, noise_seed = noise_seed + ) +} + +a1 <- denoise(1, noise_seed = 10007) +a2 <- denoise(1, noise_seed = 10007) +a3 <- denoise(1, noise_seed = 10008) +e0 <- denoise(0) +expect_true(max_abs_diff(a1$latents, a2$latents) == 0) +expect_true(max_abs_diff(a1$audio_latents, a2$audio_latents) == 0) +expect_true(max_abs_diff(a1$latents, a3$latents) > 1e-3) +expect_true(max_abs_diff(a1$latents, e0$latents) > 1e-3) +expect_true(as.logical(a1$latents$isfinite()$all()$item())) + +# A seeded ancestral run restores the caller's RNG stream +torch::torch_manual_seed(99) +invisible(denoise(1, noise_seed = 10007)) +after <- torch::torch_randn(5L) +torch::torch_manual_seed(99) +expect_true(max_abs_diff(after, torch::torch_randn(5L)) == 0) diff --git a/man/txt2vid_ltx2.Rd b/man/txt2vid_ltx2.Rd index ff5f978..3cf0642 100644 --- a/man/txt2vid_ltx2.Rd +++ b/man/txt2vid_ltx2.Rd @@ -36,7 +36,8 @@ txt2vid_ltx2( resident = character(), trim_frames = 0L, audio = NULL, - verbose = TRUE + verbose = TRUE, + sampler = c("auto", "euler", "euler_ancestral") ) } \arguments{ @@ -147,6 +148,13 @@ lines), FALSE = "silent". "progress" prints a one-line generation summary plus a denoise progress bar (interactive) or periodic step ticks (captured logs).} +\item{sampler}{One of "auto", "euler" or "euler_ancestral". "auto" +picks the sampler the checkpoint was distilled for: plain Euler +for LTX-2.3, ancestral Euler (eta 1) for LTX-2.5 and later. The +ancestral steps draw their noise from \code{seed + 10000} (stage 1) +and \code{seed + 20000} (stage 2), so the initial noise matches the +Euler run at the same seed.} + \item{text_encoder,tokenizer}{Gemma3 model and tokenizer (or paths; see \code{\link{load_gemma3_text_encoder}} and \code{\link{gemma3_tokenizer}}). Ignored when \code{prompt_embeds} From 38dd8ff1706f451e4fb962b57c81948a56a013f7 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 12:28:36 -0500 Subject: [PATCH 04/14] Add the Gemma 4 text encoder and tokenizer for LTX-2.5 LTX-2.5 replaces Gemma 3 with a fine-tuned Gemma 4 12B (gemma4-12b-ltx-v1). The trunk matches Gemma 3's, but the norms scale by w (not 1 + w), values get a scale-free RMSNorm, attention scaling is 1, full layers use head_dim 512 with one KV head and k == v plus proportional RoPE, and each layer multiplies by a stored layer_scalar. - gemma4_text_model() ports the transformers Gemma4UnifiedTextModel text stack (fused SDPA, per-type causal/sliding masks with finite minima so left-pad rows stay finite). - load_gemma4_text_encoder() streams the text weights of the LTX-2.5 single file (config from its gemma_config metadata), skipping the vision/audio towers, the connectors' text projection and the embedded tokenizer assets; optional pinning as for Gemma 3. - gemma4_tokenizer()/encode_gemma4(): ranked-merge BPE with byte fallback (sharing the Qwen merge loop), read from tokenizer.json or the encoder file's tokenizer_json tensor. Gemma 4 adds no BOS. Left pads. - encode_with_gemma4(): the LTX-2.5 encode step; same output layout as encode_with_gemma3(). Tests: hidden states match transformers to 1.2e-5 on a tiny model with every 2.5 feature (mutating any one of norm form, layer_scalar, sliding window, proportional RoPE or the value path moves it by 0.4 to 30); tokenizer ids match HF tokenizers on synthetic cases and, at home, on the real 262k-vocab tokenizer (CJK, emoji, tabs, double spaces). --- NAMESPACE | 6 + R/gemma4_text_encoder.R | 420 ++++++++++++++++++ R/tokenizer_gemma4.R | 221 +++++++++ inst/REFERENCES.md | 2 + .../fixtures/gemma4_real_tokenizer_cases.json | 1 + .../tinytest/fixtures/gemma4_text.safetensors | Bin 0 -> 19088 bytes .../fixtures/gemma4_tiny_ckpt.safetensors | Bin 0 -> 292824 bytes .../fixtures/gemma4_tiny_tokenizer.json | 1 + .../fixtures/gemma4_tokenizer_cases.json | 1 + inst/tinytest/test_gemma4.R | 84 ++++ man/encode_gemma4.Rd | 25 ++ man/encode_with_gemma4.Rd | 40 ++ man/gemma4_attention.Rd | 20 + man/gemma4_decoder_layer.Rd | 20 + man/gemma4_rms_norm.Rd | 23 + man/gemma4_text_model.Rd | 21 + man/gemma4_tokenizer.Rd | 18 + man/load_gemma4_text_encoder.Rd | 38 ++ man/tokenizer_gemma4.Rd | 14 + tools/gen_fixtures_gemma4.py | 184 ++++++++ 20 files changed, 1139 insertions(+) create mode 100644 R/gemma4_text_encoder.R create mode 100644 R/tokenizer_gemma4.R create mode 100644 inst/tinytest/fixtures/gemma4_real_tokenizer_cases.json create mode 100644 inst/tinytest/fixtures/gemma4_text.safetensors create mode 100644 inst/tinytest/fixtures/gemma4_tiny_ckpt.safetensors create mode 100644 inst/tinytest/fixtures/gemma4_tiny_tokenizer.json create mode 100644 inst/tinytest/fixtures/gemma4_tokenizer_cases.json create mode 100644 inst/tinytest/test_gemma4.R create mode 100644 man/encode_gemma4.Rd create mode 100644 man/encode_with_gemma4.Rd create mode 100644 man/gemma4_attention.Rd create mode 100644 man/gemma4_decoder_layer.Rd create mode 100644 man/gemma4_rms_norm.Rd create mode 100644 man/gemma4_text_model.Rd create mode 100644 man/gemma4_tokenizer.Rd create mode 100644 man/load_gemma4_text_encoder.Rd create mode 100644 man/tokenizer_gemma4.Rd create mode 100644 tools/gen_fixtures_gemma4.py diff --git a/NAMESPACE b/NAMESPACE index 3947d87..b19dc47 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -18,9 +18,11 @@ export(download_sd21) export(download_sdxl) export(download_zimage_turbo) export(encode_bpe) +export(encode_gemma4) export(encode_qwen) export(encode_unigram) export(encode_with_gemma3) +export(encode_with_gemma4) export(encode_with_qwen3) export(encode_with_t5) export(filename_from_prompt) @@ -70,6 +72,8 @@ export(gemma3_encode_batch) export(gemma3_quantize_nf4) export(gemma3_text_model) export(gemma3_tokenizer) +export(gemma4_text_model) +export(gemma4_tokenizer) export(img2img) export(is_blackwell_gpu) export(latents_to_video) @@ -78,6 +82,7 @@ export(load_decoder_weights) export(load_flux2_vae_decoder) export(load_gemma3_nf4) export(load_gemma3_text_encoder) +export(load_gemma4_text_encoder) export(load_model_component) export(load_pipeline) export(load_qwen3_text_encoder) @@ -239,6 +244,7 @@ export(zimage_unpatchify) S3method(print,bpe_tokenizer) S3method(print,diffuseR_resident) +S3method(print,gemma4_tokenizer) S3method(print,ltx23_checkpoint) S3method(print,qwen_tokenizer) S3method(print,unigram_tokenizer) diff --git a/R/gemma4_text_encoder.R b/R/gemma4_text_encoder.R new file mode 100644 index 0000000..48ca9f5 --- /dev/null +++ b/R/gemma4_text_encoder.R @@ -0,0 +1,420 @@ +# Gemma 4 Text Encoder for LTX-2.5 +# +# Native R torch port of the text stack of HuggingFace transformers' +# Gemma4UnifiedTextModel (Apache-2.0, models/gemma4_unified). LTX-2.5 +# ships a fine-tuned 12B (gemma4-12b-ltx-v1) whose trunk matches the +# Gemma 3 12B of LTX-2.3 (48 layers, hidden 3840, 5 sliding : 1 full), +# with these differences: +# - RMSNorm scales by w (Gemma 3: 1 + w); values get a scale-free RMSNorm +# - attention scaling is 1.0 (the q/k norms carry the scale) +# - full-attention layers: head_dim 512, one KV head, keys reused as +# values (attention_k_eq_v), "proportional" RoPE rotating a quarter of +# the dims at theta 1e6 +# - every layer multiplies its output by a stored layer_scalar +# Vision/audio towers and the KV-sharing / per-layer-input / MoE options +# are off in this checkpoint and not ported. + +#' Gemma 4 RMS normalization +#' +#' @param dim Integer. Normalized dimension. +#' @param eps Numeric. Epsilon. +#' @param with_scale Logical. Carry a learned scale (FALSE for the value +#' norm). +#' +#' @return Module whose forward(x) normalizes over the last dimension in +#' float32 and multiplies by the scale, returning \code{x}'s dtype. +#' +#' @keywords internal +gemma4_rms_norm <- torch::nn_module( + "Gemma4RMSNorm", + initialize = function(dim, eps = 1e-6, with_scale = TRUE) { + self$eps <- eps + self$with_scale <- with_scale + if (with_scale) { + self$weight <- torch::nn_parameter(torch::torch_ones(dim)) + } +}, + forward = function(x) { + xf <- x$to(dtype = torch::torch_float32()) + out <- xf * xf$pow(2)$mean(dim = -1L, keepdim = TRUE)$add(self$eps)$pow(-0.5) + if (self$with_scale) { + out <- out * self$weight$to(dtype = torch::torch_float32()) + } + out$to(dtype = x$dtype) +} +) + +# Inverse RoPE frequencies for one layer type. "proportional" rotates +# the first floor(partial * head_dim / 2) frequency pairs, computed over +# the full head_dim, and leaves the rest unrotated (zero frequency). +.gemma4_inv_freq <- function(head_dim, params) { + base <- params$rope_theta + if (identical(params$rope_type %||% "default", "proportional")) { + angles <- as.integer(floor((params$partial_rotary_factor %||% 1) * + head_dim / 2)) + rotated <- 1 / base^(seq(0, 2 * angles - 1, by = 2) / head_dim) + c(rotated, rep(0, head_dim %/% 2L - angles)) + } else { + 1 / base^(seq(0, head_dim - 1, by = 2) / head_dim) + } +} + +# cos/sin [B, S, head_dim] for integer positions [B, S] +.gemma4_rope <- function(inv_freq, position_ids, dtype) { + inv <- torch::torch_tensor(inv_freq, dtype = torch::torch_float32(), + device = position_ids$device) + freqs <- position_ids$to(dtype = torch::torch_float32())$unsqueeze(-1L) * inv + emb <- torch::torch_cat(list(freqs, freqs), dim = -1L) + list(emb$cos()$to(dtype = dtype), emb$sin()$to(dtype = dtype)) +} + +#' Gemma 4 text attention +#' +#' @param config Model configuration (see \code{\link{load_gemma4_text_encoder}}). +#' @param layer_idx Integer. 0-based layer index. +#' +#' @return Module whose forward(hidden_states, attention_mask, cos, sin) +#' returns the attention output [B, S, hidden_size]. +#' +#' @keywords internal +gemma4_attention <- torch::nn_module( + "Gemma4Attention", + initialize = function(config, layer_idx) { + full <- identical(config$layer_types[[layer_idx + 1L]], "full_attention") + self$is_sliding <- !full + self$num_heads <- config$num_attention_heads + self$head_dim <- if (full) config$global_head_dim else config$head_dim + self$k_eq_v <- isTRUE(config$attention_k_eq_v) && full + self$num_kv_heads <- if (self$k_eq_v) { + config$num_global_key_value_heads %||% config$num_key_value_heads + } else { + config$num_key_value_heads + } + eps <- config$rms_norm_eps + h <- config$hidden_size + + self$q_proj <- linear_noinit(h, self$num_heads * self$head_dim, bias = FALSE) + self$k_proj <- linear_noinit(h, self$num_kv_heads * self$head_dim, bias = FALSE) + if (!self$k_eq_v) { + self$v_proj <- linear_noinit(h, self$num_kv_heads * self$head_dim, + bias = FALSE) + } + self$o_proj <- linear_noinit(self$num_heads * self$head_dim, h, bias = FALSE) + self$q_norm <- gemma4_rms_norm(self$head_dim, eps = eps) + self$k_norm <- gemma4_rms_norm(self$head_dim, eps = eps) + self$v_norm <- gemma4_rms_norm(self$head_dim, eps = eps, with_scale = FALSE) +}, + forward = function(hidden_states, attention_mask, cos, sin) { + b <- hidden_states$shape[1] + s <- hidden_states$shape[2] + heads <- function(x, n) x$view(c(b, s, n, self$head_dim)) + cos <- cos$unsqueeze(3L) + sin <- sin$unsqueeze(3L) + rope <- function(x) x * cos + rotate_half(x) * sin + + q <- rope(self$q_norm(heads(self$q_proj(hidden_states), self$num_heads))) + k_raw <- heads(self$k_proj(hidden_states), self$num_kv_heads) + # k == v layers take the values from the raw key projection + v_raw <- if (self$k_eq_v) { + k_raw + } else { + heads(self$v_proj(hidden_states), self$num_kv_heads) + } + k <- rope(self$k_norm(k_raw)) + v <- self$v_norm(v_raw) + + q <- q$transpose(2L, 3L) + k <- repeat_kv(k$transpose(2L, 3L), self$num_heads %/% self$num_kv_heads) + v <- repeat_kv(v$transpose(2L, 3L), self$num_heads %/% self$num_kv_heads) + out <- torch::torch_scaled_dot_product_attention(q, k, v, + attn_mask = attention_mask, + scale = 1.0) + out <- out$transpose(2L, 3L)$reshape(c(b, s, self$num_heads * self$head_dim)) + self$o_proj(out) +} +) + +#' Gemma 4 decoder layer +#' +#' @param config Model configuration. +#' @param layer_idx Integer. 0-based layer index. +#' +#' @return Module whose forward(hidden_states, attention_mask, cos, sin) +#' returns the layer output, scaled by the stored \code{layer_scalar}. +#' +#' @keywords internal +gemma4_decoder_layer <- torch::nn_module( + "Gemma4DecoderLayer", + initialize = function(config, layer_idx) { + eps <- config$rms_norm_eps + h <- config$hidden_size + self$self_attn <- gemma4_attention(config, layer_idx) + self$mlp <- gemma3_mlp(config) + self$input_layernorm <- gemma4_rms_norm(h, eps = eps) + self$post_attention_layernorm <- gemma4_rms_norm(h, eps = eps) + self$pre_feedforward_layernorm <- gemma4_rms_norm(h, eps = eps) + self$post_feedforward_layernorm <- gemma4_rms_norm(h, eps = eps) + self$layer_scalar <- torch::nn_buffer(torch::torch_ones(1L)) +}, + forward = function(hidden_states, attention_mask, cos, sin) { + residual <- hidden_states + x <- self$input_layernorm(hidden_states) + x <- self$self_attn(x, attention_mask, cos, sin) + hidden_states <- residual + self$post_attention_layernorm(x) + + residual <- hidden_states + x <- self$mlp(self$pre_feedforward_layernorm(hidden_states)) + hidden_states <- residual + self$post_feedforward_layernorm(x) + hidden_states * self$layer_scalar$to(dtype = hidden_states$dtype) +} +) + +#' Gemma 4 text model +#' +#' The text stack of Gemma 4 Unified, as the LTX-2.5 text encoder. +#' +#' @param config Model configuration list: the checkpoint's +#' \code{text_config} fields (see \code{\link{load_gemma4_text_encoder}}). +#' +#' @return Module whose forward(input_ids, attention_mask, +#' output_hidden_states) returns \code{list(last_hidden_state, +#' hidden_states)}. \code{input_ids} are 0-based; \code{hidden_states} +#' follows transformers: the state entering each layer, then the +#' final-normed output (\code{num_hidden_layers + 1} entries). +#' +#' @export +gemma4_text_model <- torch::nn_module( + "Gemma4TextModel", + initialize = function(config) { + self$config <- config + self$embed_tokens <- embedding_noinit(config$vocab_size, config$hidden_size) + self$layers <- torch::nn_module_list(lapply(seq_len(config$num_hidden_layers) - 1L, + function(i) gemma4_decoder_layer(config, i))) + self$norm <- gemma4_rms_norm(config$hidden_size, eps = config$rms_norm_eps) + rp <- config$rope_parameters + self$inv_freq <- list( + sliding_attention = .gemma4_inv_freq(config$head_dim, rp$sliding_attention), + full_attention = .gemma4_inv_freq(config$global_head_dim, rp$full_attention) + ) +}, + forward = function(input_ids, attention_mask = NULL, + output_hidden_states = FALSE) { + b <- input_ids$shape[1] + s <- input_ids$shape[2] + device <- input_ids$device + w <- self$embed_tokens$weight + # The embedding scale is rounded to the weight dtype, as in transformers + # (sqrt(3840) = 61.97 becomes 62 in bfloat16) + scale <- torch::torch_tensor(sqrt(self$config$hidden_size), dtype = w$dtype, + device = w$device) + hidden_states <- self$embed_tokens(input_ids + 1L) * scale + dtype <- hidden_states$dtype + + masks <- .gemma4_masks(attention_mask, b, s, self$config$sliding_window, + dtype, device) + positions <- torch::torch_arange(0, s - 1L, device = device)$unsqueeze(1L)$expand(c(b, -1L)) + rope <- lapply(self$inv_freq, .gemma4_rope, position_ids = positions, + dtype = dtype) + + all_hidden <- NULL + for (i in seq_along(self$layers)) { + if (output_hidden_states) { + all_hidden <- c(all_hidden, list(hidden_states)) + } + type <- self$config$layer_types[[i]] + hidden_states <- self$layers[[i]](hidden_states, masks[[type]], + rope[[type]][[1]], rope[[type]][[2]]) + } + hidden_states <- self$norm(hidden_states) + if (output_hidden_states) { + all_hidden <- c(all_hidden, list(hidden_states)) + } + list(last_hidden_state = hidden_states, hidden_states = all_hidden) +} +) + +# Additive [B, 1, S, S] masks per layer type: causal, keys limited to +# attention_mask, and for sliding layers q - k < window. Masked entries +# take the dtype's finite minimum, so a fully masked row (a left-pad +# query) softmaxes to a uniform finite row instead of NaN. +.gemma4_masks <- function(attention_mask, b, s, window, dtype, device) { + q <- torch::torch_arange(0, s - 1L, device = device)$view(c(s, 1L)) + k <- torch::torch_arange(0, s - 1L, device = device)$view(c(1L, s)) + causal <- k$le(q) + sliding <- causal$logical_and((q - k)$lt(window)) + keep <- if (is.null(attention_mask)) { + torch::torch_ones(c(b, 1L, 1L, s), dtype = torch::torch_bool(), + device = device) + } else { + attention_mask$to(device = device)$gt(0)$view(c(b, 1L, 1L, s)) + } + neg <- torch::torch_finfo(dtype)$min + additive <- function(allowed) { + allowed <- keep$logical_and(allowed$view(c(1L, 1L, s, s))) + torch::torch_zeros(c(b, 1L, s, s), dtype = dtype, device = device)$ + masked_fill(allowed$logical_not(), neg) + } + list(full_attention = additive(causal), + sliding_attention = additive(sliding)) +} + +# Model configuration from a checkpoint's text_config (the LTX-2.5 file +# embeds the full HF config as gemma_config metadata) +.gemma4_build_config <- function(raw) { + tc <- raw$text_config %||% raw + unsupported <- c(num_kv_shared_layers = 0, hidden_size_per_layer_input = 0) + for (k in names(unsupported)) { + if (!is.null(tc[[k]]) && tc[[k]] != unsupported[[k]]) { + stop("Gemma 4 option ", k, " = ", tc[[k]], " is not supported") + } + } + if (isTRUE(tc$enable_moe_block)) { + stop("Gemma 4 MoE blocks are not supported") + } + list( + vocab_size = as.integer(tc$vocab_size), + hidden_size = as.integer(tc$hidden_size), + intermediate_size = as.integer(tc$intermediate_size), + num_hidden_layers = as.integer(tc$num_hidden_layers), + num_attention_heads = as.integer(tc$num_attention_heads), + num_key_value_heads = as.integer(tc$num_key_value_heads), + num_global_key_value_heads = as.integer(tc$num_global_key_value_heads %||% + tc$num_key_value_heads), + head_dim = as.integer(tc$head_dim), + global_head_dim = as.integer(tc$global_head_dim %||% 512L), + attention_k_eq_v = isTRUE(tc$attention_k_eq_v), + layer_types = as.list(tc$layer_types), + sliding_window = as.integer(tc$sliding_window), + rope_parameters = tc$rope_parameters, + rms_norm_eps = tc$rms_norm_eps %||% 1e-6 + ) +} + +# Checkpoint key -> module name: text weights only (model.* in the LTX +# file, model.language_model.* in a transformers export); NA skips the +# vision/audio towers, projectors, the connectors' text projection and +# the embedded tokenizer assets +.gemma4_map_key <- function(key) { + if (grepl("^model\\.language_model\\.", key)) { + return(sub("^model\\.language_model\\.", "", key)) + } + if (grepl("^model\\.(embed_tokens|layers|norm)\\.", key)) { + return(sub("^model\\.", "", key)) + } + NA_character_ +} + +#' Load the Gemma 4 text encoder (LTX-2.5) +#' +#' Streams the text weights of an LTX-2.5 Gemma 4 checkpoint into a +#' \code{\link{gemma4_text_model}} skeleton, one tensor at a time, so +#' the 26 GB bf16 file is never fully in memory. The vision and audio +#' towers in the same file are skipped. +#' +#' @param path Path to the LTX-2.5 text encoder file (e.g. +#' \code{gemma4-12b-with-proj-ltx-2.5-bf16.safetensors}), which embeds +#' its config as \code{gemma_config} metadata. +#' @param device Character. Device for the loaded model. +#' @param dtype Character. "bfloat16" (checkpoint native) or "float32". +#' @param pin Logical. When loading to the CPU, page-lock the weights so +#' \code{\link{encode_with_gemma4}} can move the model to the GPU per +#' encode at DMA speed. Default follows +#' \code{options(diffuseR.pin_staging)}. +#' @param verbose Logical. +#' +#' @return A \code{gemma4_text_model}. +#' +#' @export +load_gemma4_text_encoder <- function(path, device = "cpu", dtype = "bfloat16", + pin = getOption("diffuseR.pin_staging", TRUE), + verbose = TRUE) { + ckpt <- ltx23_open_checkpoint(path, require_version = NULL) + meta <- ckpt$handle$metadata[["__metadata__"]] + if (is.null(meta[["gemma_config"]])) { + stop("No gemma_config metadata in ", path, + "; expected an LTX-2.5 Gemma 4 text encoder file") + } + config <- .gemma4_build_config(jsonlite::fromJSON(meta[["gemma_config"]], + simplifyVector = FALSE)) + torch_dtype <- switch(dtype, bfloat16 = torch::torch_bfloat16(), + float32 = torch::torch_float32(), + stop("Unsupported dtype: ", dtype)) + if (verbose) { + message(sprintf("Creating Gemma 4 model: %d layers, hidden_size=%d", + config$num_hidden_layers, config$hidden_size)) + } + model <- .construct_skeleton(gemma4_text_model, config, dtype = torch_dtype) + + keys <- ckpt$keys + res <- ltx23_load_group(ckpt, keys, model, map_key = .gemma4_map_key, + verbose = verbose) + if (length(res$unmapped) || length(res$unfilled)) { + stop("Gemma 4 load: ", length(res$unmapped), " unmapped, ", + length(res$unfilled), " unfilled (e.g. ", + paste(utils::head(c(res$unmapped, res$unfilled), 3), collapse = ", "), + ")") + } + model$to(device = device) + model$eval() + + if (pin && device == "cpu") { + st <- .pin_component(model) + if (!is.null(st)) { + attr(model, "staging") <- st + if (verbose) { + message("Gemma 4 weights pinned for staged transfer") + } + } + } + model +} + +#' Encode prompts with Gemma 4 for LTX-2.5 +#' +#' The LTX-2.5 text-encoder step: strips each prompt, tokenizes with left +#' padding to \code{max_sequence_length}, and returns every hidden state +#' (embedding output plus each layer, the last final-normed) stacked on a +#' new last dimension for the connectors. +#' +#' @param prompts Character vector. +#' @param model A \code{\link{gemma4_text_model}}. A pinned CPU-resident +#' model moves to \code{device} for the encode and back afterwards. +#' @param tokenizer A \code{\link{gemma4_tokenizer}}. +#' @param max_sequence_length Integer. Padded token length. +#' @param device Character. Compute device. +#' @param verbose Logical. +#' +#' @return List with \code{prompt_embeds} \code{[batch, seq_len, +#' hidden_size, num_layers + 1]} and \code{prompt_attention_mask} +#' \code{[batch, seq_len]}, the same layout as +#' \code{\link{encode_with_gemma3}}. +#' +#' @export +encode_with_gemma4 <- function(prompts, model, tokenizer, + max_sequence_length = 1024L, device = "cuda", + verbose = TRUE) { + stopifnot(inherits(tokenizer, "gemma4_tokenizer")) + # Same staging contract as encode_with_gemma3(): arm the offload + # before the onload so a partial onload is still undone + staging <- attr(model, "staging") + if (!is.null(staging) && device != "cpu" && !.staged_on(staging, device)) { + on.exit(.staged_offload(staging), add = TRUE) + .staged_onload(staging, device) + } + tokens <- encode_gemma4(tokenizer, trimws(as.character(prompts)), + max_length = max_sequence_length) + input_ids <- torch::torch_tensor(tokens$input_ids, dtype = torch::torch_long(), + device = device) + attention_mask <- torch::torch_tensor(tokens$attention_mask, + dtype = torch::torch_long(), + device = device) + if (verbose) { + message("Encoding with Gemma 4...") + } + torch::with_no_grad({ + out <- model(input_ids, attention_mask = attention_mask, + output_hidden_states = TRUE) + }) + list(prompt_embeds = torch::torch_stack(out$hidden_states, dim = -1L), + prompt_attention_mask = attention_mask) +} diff --git a/R/tokenizer_gemma4.R b/R/tokenizer_gemma4.R new file mode 100644 index 0000000..8ab8701 --- /dev/null +++ b/R/tokenizer_gemma4.R @@ -0,0 +1,221 @@ +#' Gemma 4 SentencePiece-style BPE tokenizer +#' +#' Native R port of the HuggingFace \code{tokenizers} pipeline in the +#' Gemma 4 \code{tokenizer.json} shipped with LTX-2.5: added-token +#' literals split out first, the \code{Replace " " -> U+2581} normalizer, +#' characters mapped to vocab ids with \code{<0xHH>} byte fallback, then +#' rank-ordered BPE merges over the whole string (the \code{Split} on +#' " " pre-tokenizer never matches after the normalizer). The +#' post-processor template decides special tokens; for Gemma 4 it adds +#' none, so there is no BOS. +#' +#' @name tokenizer_gemma4 +NULL + +#' Load the Gemma 4 tokenizer +#' +#' @param source Path to a \code{tokenizer.json}, a directory holding +#' one, or an LTX-2.5 text encoder \code{.safetensors} file, which +#' carries the same JSON in its \code{tokenizer_json} tensor. +#' +#' @return A \code{gemma4_tokenizer}. +#' +#' @export +gemma4_tokenizer <- function(source) { + tj <- jsonlite::fromJSON(.gemma4_tokenizer_json(source), + simplifyVector = TRUE) + model <- tj$model + if (!identical(model$type, "BPE")) { + stop("Expected a BPE tokenizer, got: ", model$type %||% "none") + } + norm <- tj$normalizer + if (!identical(norm$type, "Replace") || is.null(norm$pattern$String)) { + stop("Unsupported normalizer: ", norm$type %||% "none", + " (expected a Replace of a literal string)") + } + + # Same integer-id layout as qwen_bpe_tokenizer(): atomic vectors only, + # since live environment bindings make every R gc slower + vocab <- unlist(model$vocab) # named int: piece -> id (0-based) + piece_names <- names(vocab) + merges <- model$merges + if (is.matrix(merges)) { + a <- merges[, 1] + b <- merges[, 2] + } else { + sp <- regexpr(" ", merges, fixed = TRUE) + a <- substr(merges, 1L, sp - 1L) + b <- substr(merges, sp + 1L, nchar(merges)) + } + id_a <- unname(vocab[match(a, piece_names)]) + id_b <- unname(vocab[match(b, piece_names)]) + id_r <- unname(vocab[match(paste0(a, b), piece_names)]) + ok <- !is.na(id_a) & !is.na(id_b) & !is.na(id_r) + key <- id_a[ok] * 2097152 + id_b[ok] # ids < 2^21: exact in doubles + ord <- order(key) + + byte_pieces <- sprintf("<0x%02X>", 0:255) + added <- tj$added_tokens + + structure( + list( + merge_key = key[ord], + merge_rank = seq_along(id_a)[ok][ord], + merge_result = id_r[ok][ord], + piece_names = piece_names, + piece_ids = unname(vocab), + byte_ids = unname(vocab[match(byte_pieces, piece_names)]), + byte_fallback = isTRUE(model$byte_fallback), + unk_id = unname(vocab[model$unk_token %||% ""]), + norm_from = norm$pattern$String, + norm_to = norm$content, + added = list2env(stats::setNames(as.list(added$id), + added$content), + parent = emptyenv()), + added_contents = added$content[order(-nchar(added$content))], + template = .gemma4_template_ids(tj$post_processor, added), + pad_id = added$id[match("", added$content)] %||% 0L + ), + class = "gemma4_tokenizer" + ) +} + +#' @export +print.gemma4_tokenizer <- function(x, ...) { + cat("\n") + cat(" vocab: ", length(x$piece_ids), "pieces,", + length(x$merge_key), "merges\n") + cat(" special:", length(x$template$prefix), "prefix /", + length(x$template$suffix), "suffix ids per sequence\n") + invisible(x) +} + +# The tokenizer.json text from a json path, a directory, or the +# tokenizer_json uint8 tensor of an LTX-2.5 encoder file +.gemma4_tokenizer_json <- function(source) { + path <- path.expand(source) + if (dir.exists(path)) { + path <- file.path(path, "tokenizer.json") + } + if (!file.exists(path)) { + stop("Tokenizer source not found: ", source) + } + if (!grepl("\\.safetensors$", path)) { + return(paste(readLines(path, warn = FALSE, encoding = "UTF-8"), + collapse = "\n")) + } + handle <- safetensors::safetensors$new(path, framework = "torch") + if (!"tokenizer_json" %in% handle$keys()) { + stop("No tokenizer_json tensor in ", path) + } + bytes <- as.raw(as.integer(as.array(handle$get_tensor("tokenizer_json")))) + txt <- rawToChar(bytes) + Encoding(txt) <- "UTF-8" + txt +} + +# Special-token ids the TemplateProcessing "single" template puts before +# and after the sequence +.gemma4_template_ids <- function(post, added) { + out <- list(prefix = integer(0), suffix = integer(0)) + single <- post$single + if (!identical(post$type, "TemplateProcessing") || is.null(single)) { + return(out) + } + seen_seq <- FALSE + for (i in seq_len(NROW(single))) { + sp <- single$SpecialToken$id[i] %||% NA + if (!is.null(single$Sequence) && !is.na(single$Sequence$id[i] %||% NA)) { + seen_seq <- TRUE + } else if (!is.na(sp)) { + id <- added$id[match(sp, added$content)] + if (seen_seq) { + out$suffix <- c(out$suffix, id) + } else { + out$prefix <- c(out$prefix, id) + } + } + } + out +} + +# Normalize one plain-text segment, map characters to ids (byte fallback +# for pieces missing from the vocab), then apply the ranked merges. +# Shares the merge loop with the Qwen tokenizer. +.gemma4_encode_segment <- function(text, tokenizer) { + if (!nzchar(text)) { + return(integer(0)) + } + text <- gsub(tokenizer$norm_from, tokenizer$norm_to, enc2utf8(text), + fixed = TRUE) + chars <- strsplit(text, "", fixed = TRUE)[[1]] + ids <- tokenizer$piece_ids[match(chars, tokenizer$piece_names)] + if (anyNA(ids)) { + parts <- as.list(ids) + for (i in which(is.na(ids))) { + if (tokenizer$byte_fallback) { + b <- as.integer(charToRaw(chars[i])) + parts[[i]] <- tokenizer$byte_ids[b + 1L] + } else { + parts[[i]] <- tokenizer$unk_id + } + } + ids <- unlist(parts) + } + as.integer(.qwen_bpe_merge_ids(ids, tokenizer)) +} + +#' Encode prompts with the Gemma 4 tokenizer +#' +#' Tokenizes, applies the post-processor's special tokens (none for +#' Gemma 4), truncates to \code{max_length} keeping the start, and pads +#' on the left with \code{}, as the LTX pipelines do. +#' +#' @param tokenizer A \code{\link{gemma4_tokenizer}}. +#' @param texts Character vector of prompts. +#' @param max_length Integer. Fixed sequence length, or NULL for ragged +#' unpadded output. +#' +#' @return List with \code{input_ids} and \code{attention_mask}: integer +#' matrices [length(texts), max_length] (ragged lists when +#' \code{max_length} is NULL). Ids are 0-based. +#' +#' @export +encode_gemma4 <- function(tokenizer, texts, max_length = 1024L) { + stopifnot(inherits(tokenizer, "gemma4_tokenizer")) + encode_one <- function(text) { + ids <- integer(0) + for (chunk in .qwen_split_added(text, tokenizer)) { + if (!is.na(chunk$id)) { + ids <- c(ids, as.integer(chunk$id)) + } else { + ids <- c(ids, .gemma4_encode_segment(chunk$text, tokenizer)) + } + } + c(tokenizer$template$prefix, ids, tokenizer$template$suffix) + } + all_ids <- lapply(as.character(texts), encode_one) + + if (is.null(max_length)) { + return(list( + input_ids = all_ids, + attention_mask = lapply(all_ids, function(x) rep(1L, length(x))) + )) + } + n <- length(all_ids) + input_ids <- matrix(as.integer(tokenizer$pad_id), nrow = n, ncol = max_length) + attention_mask <- matrix(0L, nrow = n, ncol = max_length) + for (i in seq_len(n)) { + ids <- all_ids[[i]] + if (length(ids) > max_length) { + ids <- ids[seq_len(max_length)] + } + len <- length(ids) + if (len > 0L) { + cols <- (max_length - len + 1L):max_length + input_ids[i, cols] <- ids + attention_mask[i, cols] <- 1L + } + } + list(input_ids = input_ids, attention_mask = attention_mask) +} diff --git a/inst/REFERENCES.md b/inst/REFERENCES.md index 9d71b12..066630f 100644 --- a/inst/REFERENCES.md +++ b/inst/REFERENCES.md @@ -15,6 +15,8 @@ module; this file documents the actual lineage, idea by idea. | Single-file checkpoint key layout, embedded config, `model_version` metadata | Format facts of the official Lightricks checkpoints, cross-checked against diffusers `scripts/convert_ltx2_to_diffusers.py` (Apache-2.0) | | Gemma3 text encoder | Ported from HuggingFace **transformers** (Apache-2.0) | | LTX-2.5 transformer flags (`ff_bias = FALSE` video FFN, `keyframes_abs_pos_embedding` after `proj_in`) | diffusers `models/transformers/transformer_ltx2.py` (Apache-2.0); flag values from the embedded config of the official LTX-2.5 checkpoints (format facts) | +| Gemma 4 text encoder (LTX-2.5) | Ported from HuggingFace **transformers** (Apache-2.0) `models/gemma4_unified/modeling_gemma4_unified.py` (text stack) and `modeling_rope_utils.py` (`proportional` RoPE); the LTX encode recipe (strip, left pad, all 49 hidden states stacked) from diffusers `modular_pipelines/ltx2/encoders.py` (Apache-2.0). Parity fixtures: `tools/gen_fixtures_gemma4.py` | +| Gemma 4 tokenizer | Re-implements the HuggingFace **tokenizers** BPE pipeline described by the shipped `tokenizer.json` (normalizer, byte fallback, ranked merges, post-processor template); verified against `tokenizers` ids | | Ancestral (SDE) Euler step for LTX-2.5, eta 1 | Ported from diffusers `schedulers/scheduling_ltx_euler_ancestral_rf.py` (Apache-2.0). That LTX-2.5+ checkpoints use it, and the stage noise seeds `seed + 10000` / `seed + 20000`, are numeric facts from Lightricks' LTX-2 release (`ANCESTRAL_SAMPLER_SINCE_VERSION`, LTX-2 Community License; no code taken) | ## FLUX.1-schnell diff --git a/inst/tinytest/fixtures/gemma4_real_tokenizer_cases.json b/inst/tinytest/fixtures/gemma4_real_tokenizer_cases.json new file mode 100644 index 0000000..30c52c3 --- /dev/null +++ b/inst/tinytest/fixtures/gemma4_real_tokenizer_cases.json @@ -0,0 +1 @@ +[{"text": "A golden retriever runs across a sunlit meadow, slow motion.", "ids": [236776, 13935, 102202, 8784, 3418, 496, 3768, 18114, 35280, 236764, 5111, 5776, 236761]}, {"text": "Close-up: rain drips off a neon sign that reads \"OPEN 24/7\" — cinematic.", "ids": [9564, 236772, 1048, 236787, 6927, 225274, 1135, 496, 23915, 1519, 600, 20838, 623, 38764, 236743, 236778, 236812, 236786, 236832, 236775, 2192, 60420, 236761]}, {"text": "一只猫在窗台上睡觉。", "ids": [118206, 239959, 237075, 239875, 211722, 163381, 236924]}, {"text": " two spaces and a tab\there ", "ids": [138, 13498, 138, 35220, 532, 496, 6937, 255968, 8472, 236743]}, {"text": "Emoji test 🚀🌙 and café crème brûlée", "ids": [127268, 1594, 236743, 242015, 244876, 532, 33443, 100479, 125501, 155666]}] \ No newline at end of file diff --git a/inst/tinytest/fixtures/gemma4_text.safetensors b/inst/tinytest/fixtures/gemma4_text.safetensors new file mode 100644 index 0000000000000000000000000000000000000000..1b7c48b58c5d606a3276bf421ef19f83c2957088 GIT binary patch literal 19088 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%0AgZ8g5_ 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 OK1cC+iO)rR{`nsWt@1$t literal 0 HcmV?d00001 diff --git a/inst/tinytest/fixtures/gemma4_tiny_ckpt.safetensors b/inst/tinytest/fixtures/gemma4_tiny_ckpt.safetensors new file mode 100644 index 0000000000000000000000000000000000000000..90f24ab26659fb4e1a94ff1c665874ed5f1b9fb9 GIT binary patch literal 292824 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

1%|-`;VDMfd#<9bpS(FeyEP!kw{!u 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 literal 0 HcmV?d00001 diff --git a/inst/tinytest/fixtures/gemma4_tiny_tokenizer.json b/inst/tinytest/fixtures/gemma4_tiny_tokenizer.json new file mode 100644 index 0000000..42c5488 --- /dev/null +++ b/inst/tinytest/fixtures/gemma4_tiny_tokenizer.json @@ -0,0 +1 @@ +{"version": "1.0", "truncation": null, "padding": null, "added_tokens": [{"id": 0, "content": "", "single_word": false, "lstrip": false, "rstrip": false, "normalized": false, "special": true}, {"id": 1, "content": "", "single_word": false, "lstrip": false, "rstrip": false, "normalized": false, "special": true}, {"id": 2, "content": "", "single_word": false, "lstrip": false, "rstrip": false, "normalized": false, "special": true}, {"id": 3, "content": "", "single_word": false, "lstrip": false, "rstrip": false, "normalized": false, "special": true}, {"id": 4, "content": "", "single_word": false, "lstrip": false, "rstrip": false, "normalized": false, "special": true}], "normalizer": {"type": "Replace", "pattern": {"String": " "}, "content": "▁"}, "pre_tokenizer": {"type": "Split", "pattern": {"String": " "}, "behavior": "MergedWithPrevious", "invert": false}, "post_processor": {"type": "TemplateProcessing", "single": [{"Sequence": {"id": "A", "type_id": 0}}], "pair": [{"Sequence": {"id": "A", "type_id": 0}}, {"Sequence": {"id": "B", "type_id": 1}}], "special_tokens": {}}, "decoder": null, "model": {"type": "BPE", "dropout": null, "unk_token": "", "continuing_subword_prefix": null, "end_of_word_suffix": null, "fuse_unk": true, "byte_fallback": true, "ignore_merges": false, "vocab": {"": 0, "": 1, "": 2, "": 3, "": 4, "<0x00>": 5, "<0x01>": 6, "<0x02>": 7, "<0x03>": 8, "<0x04>": 9, "<0x05>": 10, "<0x06>": 11, "<0x07>": 12, "<0x08>": 13, "<0x09>": 14, "<0x0A>": 15, "<0x0B>": 16, "<0x0C>": 17, "<0x0D>": 18, "<0x0E>": 19, "<0x0F>": 20, "<0x10>": 21, "<0x11>": 22, "<0x12>": 23, "<0x13>": 24, "<0x14>": 25, "<0x15>": 26, "<0x16>": 27, "<0x17>": 28, "<0x18>": 29, "<0x19>": 30, "<0x1A>": 31, "<0x1B>": 32, "<0x1C>": 33, "<0x1D>": 34, "<0x1E>": 35, "<0x1F>": 36, "<0x20>": 37, "<0x21>": 38, "<0x22>": 39, "<0x23>": 40, "<0x24>": 41, "<0x25>": 42, "<0x26>": 43, "<0x27>": 44, "<0x28>": 45, "<0x29>": 46, "<0x2A>": 47, "<0x2B>": 48, "<0x2C>": 49, "<0x2D>": 50, "<0x2E>": 51, "<0x2F>": 52, "<0x30>": 53, "<0x31>": 54, "<0x32>": 55, "<0x33>": 56, "<0x34>": 57, "<0x35>": 58, "<0x36>": 59, "<0x37>": 60, "<0x38>": 61, "<0x39>": 62, "<0x3A>": 63, "<0x3B>": 64, "<0x3C>": 65, "<0x3D>": 66, "<0x3E>": 67, "<0x3F>": 68, "<0x40>": 69, "<0x41>": 70, "<0x42>": 71, "<0x43>": 72, "<0x44>": 73, "<0x45>": 74, "<0x46>": 75, "<0x47>": 76, "<0x48>": 77, "<0x49>": 78, "<0x4A>": 79, "<0x4B>": 80, "<0x4C>": 81, "<0x4D>": 82, "<0x4E>": 83, "<0x4F>": 84, "<0x50>": 85, "<0x51>": 86, "<0x52>": 87, "<0x53>": 88, "<0x54>": 89, "<0x55>": 90, "<0x56>": 91, "<0x57>": 92, "<0x58>": 93, "<0x59>": 94, "<0x5A>": 95, "<0x5B>": 96, "<0x5C>": 97, "<0x5D>": 98, "<0x5E>": 99, "<0x5F>": 100, "<0x60>": 101, "<0x61>": 102, "<0x62>": 103, "<0x63>": 104, "<0x64>": 105, "<0x65>": 106, "<0x66>": 107, "<0x67>": 108, "<0x68>": 109, "<0x69>": 110, "<0x6A>": 111, "<0x6B>": 112, "<0x6C>": 113, "<0x6D>": 114, "<0x6E>": 115, "<0x6F>": 116, "<0x70>": 117, "<0x71>": 118, "<0x72>": 119, "<0x73>": 120, "<0x74>": 121, "<0x75>": 122, "<0x76>": 123, "<0x77>": 124, "<0x78>": 125, "<0x79>": 126, "<0x7A>": 127, "<0x7B>": 128, "<0x7C>": 129, "<0x7D>": 130, "<0x7E>": 131, "<0x7F>": 132, "<0x80>": 133, "<0x81>": 134, "<0x82>": 135, "<0x83>": 136, "<0x84>": 137, "<0x85>": 138, "<0x86>": 139, "<0x87>": 140, "<0x88>": 141, "<0x89>": 142, "<0x8A>": 143, "<0x8B>": 144, "<0x8C>": 145, "<0x8D>": 146, "<0x8E>": 147, "<0x8F>": 148, "<0x90>": 149, "<0x91>": 150, "<0x92>": 151, "<0x93>": 152, "<0x94>": 153, "<0x95>": 154, "<0x96>": 155, "<0x97>": 156, "<0x98>": 157, "<0x99>": 158, "<0x9A>": 159, "<0x9B>": 160, "<0x9C>": 161, "<0x9D>": 162, "<0x9E>": 163, "<0x9F>": 164, "<0xA0>": 165, "<0xA1>": 166, "<0xA2>": 167, "<0xA3>": 168, "<0xA4>": 169, "<0xA5>": 170, "<0xA6>": 171, "<0xA7>": 172, "<0xA8>": 173, "<0xA9>": 174, "<0xAA>": 175, "<0xAB>": 176, "<0xAC>": 177, "<0xAD>": 178, "<0xAE>": 179, "<0xAF>": 180, "<0xB0>": 181, "<0xB1>": 182, "<0xB2>": 183, "<0xB3>": 184, "<0xB4>": 185, "<0xB5>": 186, "<0xB6>": 187, "<0xB7>": 188, "<0xB8>": 189, "<0xB9>": 190, "<0xBA>": 191, "<0xBB>": 192, "<0xBC>": 193, "<0xBD>": 194, "<0xBE>": 195, "<0xBF>": 196, "<0xC0>": 197, "<0xC1>": 198, "<0xC2>": 199, "<0xC3>": 200, "<0xC4>": 201, "<0xC5>": 202, "<0xC6>": 203, "<0xC7>": 204, "<0xC8>": 205, "<0xC9>": 206, "<0xCA>": 207, "<0xCB>": 208, "<0xCC>": 209, "<0xCD>": 210, "<0xCE>": 211, "<0xCF>": 212, "<0xD0>": 213, "<0xD1>": 214, "<0xD2>": 215, "<0xD3>": 216, "<0xD4>": 217, "<0xD5>": 218, "<0xD6>": 219, "<0xD7>": 220, "<0xD8>": 221, "<0xD9>": 222, "<0xDA>": 223, "<0xDB>": 224, "<0xDC>": 225, "<0xDD>": 226, "<0xDE>": 227, "<0xDF>": 228, "<0xE0>": 229, "<0xE1>": 230, "<0xE2>": 231, "<0xE3>": 232, "<0xE4>": 233, "<0xE5>": 234, "<0xE6>": 235, "<0xE7>": 236, "<0xE8>": 237, "<0xE9>": 238, "<0xEA>": 239, "<0xEB>": 240, "<0xEC>": 241, "<0xED>": 242, "<0xEE>": 243, "<0xEF>": 244, "<0xF0>": 245, "<0xF1>": 246, "<0xF2>": 247, "<0xF3>": 248, "<0xF4>": 249, "<0xF5>": 250, "<0xF6>": 251, "<0xF7>": 252, "<0xF8>": 253, "<0xF9>": 254, "<0xFA>": 255, "<0xFB>": 256, "<0xFC>": 257, "<0xFD>": 258, "<0xFE>": 259, "<0xFF>": 260, "a": 261, "b": 262, "c": 263, "d": 264, "e": 265, "h": 266, "i": 267, "l": 268, "m": 269, "n": 270, "o": 271, "r": 272, "s": 273, "t": 274, "w": 275, ".": 276, "▁": 277, "▁t": 278, "he": 279, "▁the": 280, "ll": 281, "ell": 282, "ow": 283, "▁c": 284, "▁ca": 285, "at": 286, "▁cat": 287, "▁s": 288, "it": 289, "▁sit": 290, "▁o": 291, "n▁": 292}, "merges": [["▁", "t"], ["h", "e"], ["▁t", "he"], ["l", "l"], ["e", "ll"], ["o", "w"], ["▁", "c"], ["▁c", "a"], ["a", "t"], ["▁ca", "t"], ["▁", "s"], ["i", "t"], ["▁s", "it"], ["▁", "o"], ["n", "▁"]]}} \ No newline at end of file diff --git a/inst/tinytest/fixtures/gemma4_tokenizer_cases.json b/inst/tinytest/fixtures/gemma4_tokenizer_cases.json new file mode 100644 index 0000000..211f8fa --- /dev/null +++ b/inst/tinytest/fixtures/gemma4_tokenizer_cases.json @@ -0,0 +1 @@ +[{"text": "the cat sat", "ids": [274, 279, 287, 288, 286]}, {"text": "hello cat", "ids": [279, 281, 271, 287]}, {"text": " the cat ", "ids": [280, 277, 287, 277]}, {"text": "tell", "ids": [274, 282]}, {"text": "caté ok", "ids": [263, 286, 200, 174, 291, 112]}, {"text": "thecat", "ids": [274, 279, 1, 263, 286]}, {"text": "sit on the mat.", "ids": [273, 289, 291, 270, 280, 277, 269, 286, 276]}] \ No newline at end of file diff --git a/inst/tinytest/test_gemma4.R b/inst/tinytest/test_gemma4.R new file mode 100644 index 0000000..e779015 --- /dev/null +++ b/inst/tinytest/test_gemma4.R @@ -0,0 +1,84 @@ +# Parity of the Gemma 4 (LTX-2.5) text encoder and tokenizer against +# HF transformers / tokenizers fixtures (tools/gen_fixtures_gemma4.py). + +if (!requireNamespace("torch", quietly = TRUE) || !torch::torch_is_installed()) { + exit_file("torch not fully installed") +} +if (!requireNamespace("safetensors", quietly = TRUE)) { + exit_file("safetensors not installed") +} + +library(diffuseR) + +fixture <- function(name) { + p <- system.file("tinytest", "fixtures", name, package = "diffuseR") + if (p == "") p <- file.path("fixtures", name) + p +} +if (!file.exists(fixture("gemma4_text.safetensors"))) exit_file("gemma4 fixtures missing") +max_abs_diff <- function(a, b) as.numeric((a - b)$abs()$max()) + +# --- Tokenizer ------------------------------------------------------------------- + +tok <- gemma4_tokenizer(fixture("gemma4_tiny_tokenizer.json")) +expect_inherits(tok, "gemma4_tokenizer") +# Gemma 4's post-processor adds no special tokens (no BOS) +expect_equal(length(tok$template$prefix) + length(tok$template$suffix), 0L) +cases <- jsonlite::fromJSON(fixture("gemma4_tokenizer_cases.json"), + simplifyVector = FALSE) +for (cs in cases) { + got <- encode_gemma4(tok, cs$text, max_length = NULL)$input_ids[[1]] + expect_identical(as.integer(got), as.integer(unlist(cs$ids)), info = cs$text) +} +# Left padding with and truncation keeping the start +enc <- encode_gemma4(tok, c("the cat sat", "tell"), max_length = 4L) +expect_identical(enc$input_ids[2, ], c(0L, 0L, 274L, 282L)) +expect_identical(enc$attention_mask[2, ], c(0L, 0L, 1L, 1L)) +expect_identical(enc$input_ids[1, ], c(274L, 279L, 287L, 288L)) + +# The real LTX-2.5 tokenizer, when a copy is available locally +real_tok <- Sys.getenv("GEMMA4_TOKENIZER_JSON") +if (at_home() && nzchar(real_tok) && file.exists(real_tok)) { + rtok <- gemma4_tokenizer(real_tok) + rcases <- jsonlite::fromJSON(fixture("gemma4_real_tokenizer_cases.json"), + simplifyVector = FALSE) + for (cs in rcases) { + got <- encode_gemma4(rtok, cs$text, max_length = NULL)$input_ids[[1]] + expect_identical(as.integer(got), as.integer(unlist(cs$ids)), info = cs$text) + } +} + +# --- Model through the single-file loader --------------------------------------- + +model <- load_gemma4_text_encoder(fixture("gemma4_tiny_ckpt.safetensors"), + dtype = "float32", pin = FALSE, verbose = FALSE) +expect_inherits(model, "Gemma4TextModel") +blk_full <- model$layers[[3]]$self_attn +expect_true(blk_full$k_eq_v) +expect_null(blk_full$v_proj) +expect_equal(blk_full$head_dim, 16L) +expect_equal(blk_full$num_kv_heads, 1L) +expect_false(model$layers[[1]]$self_attn$k_eq_v) + +fx <- safetensors::safe_load_file(fixture("gemma4_text.safetensors"), + framework = "torch") +torch::with_no_grad({ + out <- model(fx$input_ids, attention_mask = fx$attention_mask, + output_hidden_states = TRUE) +}) +stacked <- torch::torch_stack(out$hidden_states, dim = -1L) +expect_equal(as.integer(stacked$shape), as.integer(fx$hidden_states$shape)) +# Real tokens are the contract (the connectors replace pad positions) +valid <- fx$attention_mask$to(dtype = torch::torch_bool()) +expect_true(max_abs_diff(stacked[valid], fx$hidden_states[valid]) < 1e-4) +expect_true(max_abs_diff(out$last_hidden_state[valid], + fx$last_hidden_state[valid]) < 1e-4) +expect_true(as.logical(stacked$isfinite()$all()$item())) + +# The full pipeline step: left-padded encode returns the same stack layout +enc <- encode_with_gemma4(c("the cat sat", "tell"), model, tok, + max_sequence_length = 6L, device = "cpu", + verbose = FALSE) +expect_equal(as.integer(enc$prompt_embeds$shape), c(2L, 6L, 32L, 7L)) +expect_identical(as.integer(as.array(enc$prompt_attention_mask[2, ])), + c(0L, 0L, 0L, 0L, 1L, 1L)) diff --git a/man/encode_gemma4.Rd b/man/encode_gemma4.Rd new file mode 100644 index 0000000..4509299 --- /dev/null +++ b/man/encode_gemma4.Rd @@ -0,0 +1,25 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{encode_gemma4} +\alias{encode_gemma4} +\title{Encode prompts with the Gemma 4 tokenizer} +\usage{ +encode_gemma4(tokenizer, texts, max_length = 1024L) +} +\arguments{ +\item{tokenizer}{A \code{\link{gemma4_tokenizer}}.} + +\item{texts}{Character vector of prompts.} + +\item{max_length}{Integer. Fixed sequence length, or NULL for ragged +unpadded output.} +} +\value{ +List with \code{input_ids} and \code{attention_mask}: integer + matrices [length(texts), max_length] (ragged lists when + \code{max_length} is NULL). Ids are 0-based. +} +\description{ +Tokenizes, applies the post-processor's special tokens (none for +Gemma 4), truncates to \code{max_length} keeping the start, and pads +on the left with \code{}, as the LTX pipelines do. +} diff --git a/man/encode_with_gemma4.Rd b/man/encode_with_gemma4.Rd new file mode 100644 index 0000000..fb6130e --- /dev/null +++ b/man/encode_with_gemma4.Rd @@ -0,0 +1,40 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{encode_with_gemma4} +\alias{encode_with_gemma4} +\title{Encode prompts with Gemma 4 for LTX-2.5} +\usage{ +encode_with_gemma4( + prompts, + model, + tokenizer, + max_sequence_length = 1024L, + device = "cuda", + verbose = TRUE +) +} +\arguments{ +\item{prompts}{Character vector.} + +\item{model}{A \code{\link{gemma4_text_model}}. A pinned CPU-resident +model moves to \code{device} for the encode and back afterwards.} + +\item{tokenizer}{A \code{\link{gemma4_tokenizer}}.} + +\item{max_sequence_length}{Integer. Padded token length.} + +\item{device}{Character. Compute device.} + +\item{verbose}{Logical.} +} +\value{ +List with \code{prompt_embeds} \code{[batch, seq_len, + hidden_size, num_layers + 1]} and \code{prompt_attention_mask} + \code{[batch, seq_len]}, the same layout as + \code{\link{encode_with_gemma3}}. +} +\description{ +The LTX-2.5 text-encoder step: strips each prompt, tokenizes with left +padding to \code{max_sequence_length}, and returns every hidden state +(embedding output plus each layer, the last final-normed) stacked on a +new last dimension for the connectors. +} diff --git a/man/gemma4_attention.Rd b/man/gemma4_attention.Rd new file mode 100644 index 0000000..4c5fce6 --- /dev/null +++ b/man/gemma4_attention.Rd @@ -0,0 +1,20 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{gemma4_attention} +\alias{gemma4_attention} +\title{Gemma 4 text attention} +\usage{ +gemma4_attention(config, layer_idx) +} +\arguments{ +\item{config}{Model configuration (see \code{\link{load_gemma4_text_encoder}}).} + +\item{layer_idx}{Integer. 0-based layer index.} +} +\value{ +Module whose forward(hidden_states, attention_mask, cos, sin) + returns the attention output [B, S, hidden_size]. +} +\description{ +Gemma 4 text attention +} +\keyword{internal} diff --git a/man/gemma4_decoder_layer.Rd b/man/gemma4_decoder_layer.Rd new file mode 100644 index 0000000..8bcc045 --- /dev/null +++ b/man/gemma4_decoder_layer.Rd @@ -0,0 +1,20 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{gemma4_decoder_layer} +\alias{gemma4_decoder_layer} +\title{Gemma 4 decoder layer} +\usage{ +gemma4_decoder_layer(config, layer_idx) +} +\arguments{ +\item{config}{Model configuration.} + +\item{layer_idx}{Integer. 0-based layer index.} +} +\value{ +Module whose forward(hidden_states, attention_mask, cos, sin) + returns the layer output, scaled by the stored \code{layer_scalar}. +} +\description{ +Gemma 4 decoder layer +} +\keyword{internal} diff --git a/man/gemma4_rms_norm.Rd b/man/gemma4_rms_norm.Rd new file mode 100644 index 0000000..e20e65a --- /dev/null +++ b/man/gemma4_rms_norm.Rd @@ -0,0 +1,23 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{gemma4_rms_norm} +\alias{gemma4_rms_norm} +\title{Gemma 4 RMS normalization} +\usage{ +gemma4_rms_norm(dim, eps = 1e-06, with_scale = TRUE) +} +\arguments{ +\item{dim}{Integer. Normalized dimension.} + +\item{eps}{Numeric. Epsilon.} + +\item{with_scale}{Logical. Carry a learned scale (FALSE for the value +norm).} +} +\value{ +Module whose forward(x) normalizes over the last dimension in + float32 and multiplies by the scale, returning \code{x}'s dtype. +} +\description{ +Gemma 4 RMS normalization +} +\keyword{internal} diff --git a/man/gemma4_text_model.Rd b/man/gemma4_text_model.Rd new file mode 100644 index 0000000..fc5aaaf --- /dev/null +++ b/man/gemma4_text_model.Rd @@ -0,0 +1,21 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{gemma4_text_model} +\alias{gemma4_text_model} +\title{Gemma 4 text model} +\usage{ +gemma4_text_model(config) +} +\arguments{ +\item{config}{Model configuration list: the checkpoint's +\code{text_config} fields (see \code{\link{load_gemma4_text_encoder}}).} +} +\value{ +Module whose forward(input_ids, attention_mask, + output_hidden_states) returns \code{list(last_hidden_state, + hidden_states)}. \code{input_ids} are 0-based; \code{hidden_states} + follows transformers: the state entering each layer, then the + final-normed output (\code{num_hidden_layers + 1} entries). +} +\description{ +The text stack of Gemma 4 Unified, as the LTX-2.5 text encoder. +} diff --git a/man/gemma4_tokenizer.Rd b/man/gemma4_tokenizer.Rd new file mode 100644 index 0000000..8e07872 --- /dev/null +++ b/man/gemma4_tokenizer.Rd @@ -0,0 +1,18 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{gemma4_tokenizer} +\alias{gemma4_tokenizer} +\title{Load the Gemma 4 tokenizer} +\usage{ +gemma4_tokenizer(source) +} +\arguments{ +\item{source}{Path to a \code{tokenizer.json}, a directory holding +one, or an LTX-2.5 text encoder \code{.safetensors} file, which +carries the same JSON in its \code{tokenizer_json} tensor.} +} +\value{ +A \code{gemma4_tokenizer}. +} +\description{ +Load the Gemma 4 tokenizer +} diff --git a/man/load_gemma4_text_encoder.Rd b/man/load_gemma4_text_encoder.Rd new file mode 100644 index 0000000..5c7c9db --- /dev/null +++ b/man/load_gemma4_text_encoder.Rd @@ -0,0 +1,38 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{load_gemma4_text_encoder} +\alias{load_gemma4_text_encoder} +\title{Load the Gemma 4 text encoder (LTX-2.5)} +\usage{ +load_gemma4_text_encoder( + path, + device = "cpu", + dtype = "bfloat16", + pin = getOption("diffuseR.pin_staging", TRUE), + verbose = TRUE +) +} +\arguments{ +\item{path}{Path to the LTX-2.5 text encoder file (e.g. +\code{gemma4-12b-with-proj-ltx-2.5-bf16.safetensors}), which embeds +its config as \code{gemma_config} metadata.} + +\item{device}{Character. Device for the loaded model.} + +\item{dtype}{Character. "bfloat16" (checkpoint native) or "float32".} + +\item{pin}{Logical. When loading to the CPU, page-lock the weights so +\code{\link{encode_with_gemma4}} can move the model to the GPU per +encode at DMA speed. Default follows +\code{options(diffuseR.pin_staging)}.} + +\item{verbose}{Logical.} +} +\value{ +A \code{gemma4_text_model}. +} +\description{ +Streams the text weights of an LTX-2.5 Gemma 4 checkpoint into a +\code{\link{gemma4_text_model}} skeleton, one tensor at a time, so +the 26 GB bf16 file is never fully in memory. The vision and audio +towers in the same file are skipped. +} diff --git a/man/tokenizer_gemma4.Rd b/man/tokenizer_gemma4.Rd new file mode 100644 index 0000000..326840f --- /dev/null +++ b/man/tokenizer_gemma4.Rd @@ -0,0 +1,14 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{tokenizer_gemma4} +\alias{tokenizer_gemma4} +\title{Gemma 4 SentencePiece-style BPE tokenizer} +\description{ +Native R port of the HuggingFace \code{tokenizers} pipeline in the +Gemma 4 \code{tokenizer.json} shipped with LTX-2.5: added-token +literals split out first, the \code{Replace " " -> U+2581} normalizer, +characters mapped to vocab ids with \code{<0xHH>} byte fallback, then +rank-ordered BPE merges over the whole string (the \code{Split} on +" " pre-tokenizer never matches after the normalizer). The +post-processor template decides special tokens; for Gemma 4 it adds +none, so there is no BOS. +} diff --git a/tools/gen_fixtures_gemma4.py b/tools/gen_fixtures_gemma4.py new file mode 100644 index 0000000..37d9dcb --- /dev/null +++ b/tools/gen_fixtures_gemma4.py @@ -0,0 +1,184 @@ +# Generate Gemma 4 (LTX-2.5 text encoder) parity fixtures for the R port. +# +# Uses HF transformers' Gemma4UnifiedTextModel (Apache-2.0) with a tiny +# random-init config that keeps every LTX-2.5 feature: 5:1-style sliding +# and full layers, full layers with a doubled head_dim, one KV head and +# k == v, proportional RoPE with partial_rotary_factor 0.25, v_norm and +# per-layer layer_scalar. All weights are perturbed off their init so +# norm scales (x * w, not x * (1 + w)) and layer scalars are exercised. +# +# Also builds a tiny tokenizer.json with the Gemma 4 pipeline (Replace +# " " -> U+2581, byte fallback, no BOS template) and records HF tokenizers +# ids for a set of prompts. Run via tools/gen_fixtures.sh (needs the +# tokenizers wheel, which transformers depends on). + +import json +import os + +import torch +from safetensors.torch import save_file +from tokenizers import Tokenizer +from transformers import Gemma4UnifiedTextConfig, Gemma4UnifiedTextModel + +OUT_DIR = os.path.join(os.path.dirname(__file__), "..", "inst", "tinytest", "fixtures") +os.makedirs(OUT_DIR, exist_ok=True) + +torch.manual_seed(54) + +layer_types = ["sliding_attention", "sliding_attention", "full_attention", + "sliding_attention", "sliding_attention", "full_attention"] +text_config = dict( + vocab_size=300, # covers the tiny tokenizer below, for the encode test + hidden_size=32, + intermediate_size=64, + num_hidden_layers=len(layer_types), + num_attention_heads=4, + num_key_value_heads=2, + head_dim=8, + global_head_dim=16, + num_global_key_value_heads=1, + attention_k_eq_v=True, + layer_types=layer_types, + sliding_window=3, + rope_parameters={ + "full_attention": {"rope_type": "proportional", + "partial_rotary_factor": 0.25, + "rope_theta": 1_000_000.0}, + "sliding_attention": {"rope_type": "default", "rope_theta": 10_000.0}, + }, + hidden_activation="gelu_pytorch_tanh", + rms_norm_eps=1e-6, + max_position_embeddings=512, + num_kv_shared_layers=0, + hidden_size_per_layer_input=0, + enable_moe_block=False, + use_double_wide_mlp=False, + pad_token_id=0, +) +config = Gemma4UnifiedTextConfig(**text_config) +config._attn_implementation = "eager" +model = Gemma4UnifiedTextModel(config) +model.eval() + +with torch.no_grad(): + for name, p in model.named_parameters(): + if name.endswith("norm.weight"): + p.copy_(1.0 + 0.3 * torch.randn_like(p)) + else: + p.add_(0.05 * torch.randn_like(p)) + for layer in model.layers: + layer.layer_scalar.copy_(0.5 + torch.rand_like(layer.layer_scalar)) + +# Left-padded batch (LTX pads on the left), longer than the sliding +# window so the window mask binds +input_ids = torch.tensor([ + [5, 23, 61, 7, 19, 88, 42, 3, 9], + [0, 0, 0, 33, 14, 2, 71, 55, 12], +]) +attention_mask = (input_ids != 0).long() +attention_mask[0] = 1 + +with torch.no_grad(): + out = model(input_ids=input_ids, attention_mask=attention_mask, + output_hidden_states=True, use_cache=False) +hidden = torch.stack(out.hidden_states, dim=-1) # [B, S, H, L + 1] + +fx = { + "input_ids": input_ids, + "attention_mask": attention_mask, + "hidden_states": hidden, + "last_hidden_state": out.last_hidden_state, +} +fx = {k: v.contiguous() for k, v in fx.items()} +save_file(fx, os.path.join(OUT_DIR, "gemma4_text.safetensors"), + metadata={"purpose": "diffuseR Gemma 4 test fixture"}) +print(f"wrote {len(fx)} tensors to {OUT_DIR}/gemma4_text.safetensors") + +# Tiny checkpoint in the layout of the LTX-2.5 bundled encoder file: +# model.* text weights, the connectors' text projection, a vision tensor +# the text-only loader must skip, and gemma_config metadata +ckpt = {f"model.{k}": v.detach().clone().contiguous() + for k, v in model.state_dict().items()} +ckpt["text_embedding_projection.video_aggregate_embed.weight"] = torch.randn(8, 32 * 7) +ckpt["text_embedding_projection.video_aggregate_embed.bias"] = torch.randn(8) +ckpt["vision_model.patch_dense.weight"] = torch.randn(4, 4) +gemma_config = {"architectures": ["Gemma4UnifiedForConditionalGeneration"], + "gemma_version": "gemma4-12b-ltx-v1", + "text_config": text_config} +save_file(ckpt, os.path.join(OUT_DIR, "gemma4_tiny_ckpt.safetensors"), + metadata={"gemma_config": json.dumps(gemma_config), "format": "pt"}) +print(f"wrote {len(ckpt)} tensors to {OUT_DIR}/gemma4_tiny_ckpt.safetensors") + +# --- Tokenizer ------------------------------------------------------------------ + +# Pieces: specials at the Gemma ids, the 256 byte-fallback tokens, single +# characters, and merged pieces reachable through the merge list +specials = ["", "", "", "", ""] +chars = list("abcdehilmnorstw.") + ["▁"] +merges = [("▁", "t"), ("h", "e"), ("▁t", "he"), ("l", "l"), + ("e", "ll"), ("o", "w"), ("▁", "c"), ("▁c", "a"), + ("a", "t"), ("▁ca", "t"), ("▁", "s"), ("i", "t"), + ("▁s", "it"), ("▁", "o"), ("n", "▁")] +vocab = {} +for s in specials: + vocab[s] = len(vocab) +for b in range(256): + vocab[f"<0x{b:02X}>"] = len(vocab) +for c in chars: + vocab.setdefault(c, len(vocab)) +for a, b in merges: + vocab.setdefault(a + b, len(vocab)) + +tok_json = { + "version": "1.0", + "truncation": None, + "padding": None, + "added_tokens": [{"id": vocab[s], "content": s, "single_word": False, + "lstrip": False, "rstrip": False, "normalized": False, + "special": True} for s in specials], + "normalizer": {"type": "Replace", "pattern": {"String": " "}, + "content": "▁"}, + "pre_tokenizer": {"type": "Split", "pattern": {"String": " "}, + "behavior": "MergedWithPrevious", "invert": False}, + "post_processor": {"type": "TemplateProcessing", + "single": [{"Sequence": {"id": "A", "type_id": 0}}], + "pair": [{"Sequence": {"id": "A", "type_id": 0}}, + {"Sequence": {"id": "B", "type_id": 1}}], + "special_tokens": {}}, + "decoder": None, + "model": {"type": "BPE", "dropout": None, "unk_token": "", + "continuing_subword_prefix": None, "end_of_word_suffix": None, + "fuse_unk": True, "byte_fallback": True, "ignore_merges": False, + "vocab": vocab, "merges": [[a, b] for a, b in merges]}, +} +tok_path = os.path.join(OUT_DIR, "gemma4_tiny_tokenizer.json") +with open(tok_path, "w", encoding="utf-8") as f: + json.dump(tok_json, f, ensure_ascii=False) + +tok = Tokenizer.from_file(tok_path) +prompts = ["the cat sat", "hello cat", " the cat ", "tell", "caté ok", + "thecat", "sit on the mat."] +cases = [{"text": p, "ids": tok.encode(p, add_special_tokens=True).ids} + for p in prompts] +with open(os.path.join(OUT_DIR, "gemma4_tokenizer_cases.json"), "w", + encoding="utf-8") as f: + json.dump(cases, f, ensure_ascii=False) +print(f"wrote {len(cases)} tokenizer cases") + +# Optional: ids from the real LTX-2.5 tokenizer, for an at-home R test +real = os.environ.get("GEMMA4_TOKENIZER_JSON") +if real and os.path.exists(real): + rtok = Tokenizer.from_file(real) + rprompts = [ + "A golden retriever runs across a sunlit meadow, slow motion.", + "Close-up: rain drips off a neon sign that reads \"OPEN 24/7\" — cinematic.", + "一只猫在窗台上睡觉。", + " two spaces and a tab\there ", + "Emoji test \U0001F680\U0001F319 and café crème brûlée", + ] + rcases = [{"text": p, "ids": rtok.encode(p, add_special_tokens=True).ids} + for p in rprompts] + with open(os.path.join(OUT_DIR, "gemma4_real_tokenizer_cases.json"), "w", + encoding="utf-8") as f: + json.dump(rcases, f, ensure_ascii=False) + print(f"wrote {len(rcases)} real-tokenizer cases") From 1c1b5626334f36b2554fe122cddce3d79af5f3f4 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 12:57:55 -0500 Subject: [PATCH 05/14] Fix the Gemma 4 bf16 default path and prompt stripping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-ups to 38dd8ff: - R torch 0.17's torch_finfo() rejects bfloat16, so the default-dtype load failed on the first encode. The mask uses .gemma4_dtype_min(): bfloat16's finite minimum, torch_finfo() for the other float types. - encode_with_gemma4() strips with Python str.strip() semantics (.py_strip), removing Unicode spaces base trimws() keeps. - Tests: bf16 load and encode with fully padded rows stays finite and within 5% of float32; the bf16 mask minimum is finite in bf16; the stripping matches Python's whitespace set and " tell " encodes exactly like "tell". --- R/gemma4_text_encoder.R | 27 ++++++++++++++++++++++++--- inst/tinytest/test_gemma4.R | 34 ++++++++++++++++++++++++++++++++++ man/encode_with_gemma4.Rd | 3 ++- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/R/gemma4_text_encoder.R b/R/gemma4_text_encoder.R index 48ca9f5..3da20e1 100644 --- a/R/gemma4_text_encoder.R +++ b/R/gemma4_text_encoder.R @@ -248,7 +248,7 @@ gemma4_text_model <- torch::nn_module( } else { attention_mask$to(device = device)$gt(0)$view(c(b, 1L, 1L, s)) } - neg <- torch::torch_finfo(dtype)$min + neg <- .gemma4_dtype_min(dtype) additive <- function(allowed) { allowed <- keep$logical_and(allowed$view(c(1L, 1L, s, s))) torch::torch_zeros(c(b, 1L, s, s), dtype = dtype, device = device)$ @@ -258,6 +258,26 @@ gemma4_text_model <- torch::nn_module( sliding_attention = additive(sliding)) } +# Most negative finite value of a float dtype. R torch's torch_finfo() +# rejects bfloat16, whose minimum is -(2 - 2^-7) * 2^127 (float32's +# minimum would round to -Inf there). +.gemma4_dtype_min <- function(dtype) { + if (identical(dtype$.type(), "BFloat16")) { + return(-(2 - 2^-7) * 2^127) + } + torch::torch_finfo(dtype)$min +} + +# Python str.strip(): trims every character str.isspace() accepts, which +# includes Unicode spaces (no-break, ideographic, ...) that base +# trimws() keeps +.py_strip <- function(x) { + ws <- intToUtf8(c(9:13, 28:32, 0x85, 0xA0, 0x1680, 0x2000:0x200A, + 0x2028, 0x2029, 0x202F, 0x205F, 0x3000), multiple = TRUE) + cls <- paste0("[", paste(ws, collapse = ""), "]") + gsub(paste0("^", cls, "+|", cls, "+$"), "", enc2utf8(x), perl = TRUE) +} + # Model configuration from a checkpoint's text_config (the LTX-2.5 file # embeds the full HF config as gemma_config metadata) .gemma4_build_config <- function(raw) { @@ -371,7 +391,8 @@ load_gemma4_text_encoder <- function(path, device = "cpu", dtype = "bfloat16", #' Encode prompts with Gemma 4 for LTX-2.5 #' -#' The LTX-2.5 text-encoder step: strips each prompt, tokenizes with left +#' The LTX-2.5 text-encoder step: strips each prompt (Python +#' \code{str.strip()} semantics, Unicode spaces included), tokenizes with left #' padding to \code{max_sequence_length}, and returns every hidden state #' (embedding output plus each layer, the last final-normed) stacked on a #' new last dimension for the connectors. @@ -401,7 +422,7 @@ encode_with_gemma4 <- function(prompts, model, tokenizer, on.exit(.staged_offload(staging), add = TRUE) .staged_onload(staging, device) } - tokens <- encode_gemma4(tokenizer, trimws(as.character(prompts)), + tokens <- encode_gemma4(tokenizer, .py_strip(as.character(prompts)), max_length = max_sequence_length) input_ids <- torch::torch_tensor(tokens$input_ids, dtype = torch::torch_long(), device = device) diff --git a/inst/tinytest/test_gemma4.R b/inst/tinytest/test_gemma4.R index e779015..559aa3e 100644 --- a/inst/tinytest/test_gemma4.R +++ b/inst/tinytest/test_gemma4.R @@ -82,3 +82,37 @@ enc <- encode_with_gemma4(c("the cat sat", "tell"), model, tok, expect_equal(as.integer(enc$prompt_embeds$shape), c(2L, 6L, 32L, 7L)) expect_identical(as.integer(as.array(enc$prompt_attention_mask[2, ])), c(0L, 0L, 0L, 0L, 1L, 1L)) + +# --- Prompt stripping matches Python str.strip() -------------------------------- + +strip <- diffuseR:::.py_strip +expect_identical(strip(" tell \t\n"), "tell") +expect_identical(strip(c(" a b ", " x ")), c("a b", "x")) +expect_identical(strip("in side"), "in side") +enc_ws <- encode_with_gemma4(" tell ", model, tok, + max_sequence_length = 6L, device = "cpu", + verbose = FALSE) +enc_plain <- encode_with_gemma4("tell", model, tok, max_sequence_length = 6L, + device = "cpu", verbose = FALSE) +expect_true(torch::torch_equal(enc_ws$prompt_attention_mask, + enc_plain$prompt_attention_mask)) +expect_true(max_abs_diff(enc_ws$prompt_embeds, enc_plain$prompt_embeds) == 0) + +# --- Default (bfloat16) loader dtype -------------------------------------------- + +# The bf16 mask minimum must be finite in bf16 (float32's would be -Inf) +bmin <- diffuseR:::.gemma4_dtype_min(torch::torch_bfloat16()) +expect_true(is.finite(as.numeric(torch::torch_tensor(bmin, + dtype = torch::torch_bfloat16())$to(dtype = torch::torch_float32())))) + +model_bf <- load_gemma4_text_encoder(fixture("gemma4_tiny_ckpt.safetensors"), + pin = FALSE, verbose = FALSE) +expect_equal(model_bf$embed_tokens$weight$dtype$.type(), "BFloat16") +# "tell" in 6 slots leaves fully padded query rows: they must stay finite +enc_bf <- encode_with_gemma4(c("the cat sat", "tell"), model_bf, tok, + max_sequence_length = 6L, device = "cpu", + verbose = FALSE) +emb_bf <- enc_bf$prompt_embeds$to(dtype = torch::torch_float32()) +expect_true(as.logical(emb_bf$isfinite()$all()$item())) +ref <- enc$prompt_embeds +expect_true(max_abs_diff(emb_bf, ref) / as.numeric(ref$abs()$max()) < 0.05) diff --git a/man/encode_with_gemma4.Rd b/man/encode_with_gemma4.Rd index fb6130e..1536413 100644 --- a/man/encode_with_gemma4.Rd +++ b/man/encode_with_gemma4.Rd @@ -33,7 +33,8 @@ List with \code{prompt_embeds} \code{[batch, seq_len, \code{\link{encode_with_gemma3}}. } \description{ -The LTX-2.5 text-encoder step: strips each prompt, tokenizes with left +The LTX-2.5 text-encoder step: strips each prompt (Python +\code{str.strip()} semantics, Unicode spaces included), tokenizes with left padding to \code{max_sequence_length}, and returns every hidden state (embedding output plus each layer, the last final-normed) stacked on a new last dimension for the connectors. From aff33e3a7dcec1574b82c8f143ea63ac3d1cbf9d Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 13:02:25 -0500 Subject: [PATCH 06/14] Quantize the LTX-2.5 Gemma 4 encoder to NF4 The 12B encoder is 24 GB in bf16, which fits neither the 16 GB card nor the free host RAM. gemma4_quantize_nf4() streams the LTX-2.5 file and stores the 328 per-layer projections as NF4 (6.13 GB) with embeddings, norms and layer_scalar resident (2.01 GB). The artifact also keeps the connectors' text_embedding_projection (2.31 GB) and writes tokenizer.json, so the 26 GB source is not needed afterwards. load_gemma4_nf4() rebuilds the model (buffers included) and load_gemma4_text_encoder() dispatches to it for an artifact directory. Tests (tiny LTX-layout checkpoint, now carrying tokenizer_json): 40 casts; tokenizer from the file and from the artifact; projection kept, towers dropped; idempotent re-quantize; residents equal the source at the stored dtype; per-weight NF4 error under 0.15; the NF4 model equals a dense twin carrying its dequantized weights (< 1e-5); bf16 encode stays finite; a missing layer_scalar is a hard error; no temp leftovers. --- NAMESPACE | 2 + R/gemma4_text_encoder.R | 8 +- R/quantize_gemma4.R | 222 ++++++++++++++++++ .../fixtures/gemma4_tiny_ckpt.safetensors | Bin 292824 -> 298619 bytes inst/tinytest/test_gemma4_nf4.R | 136 +++++++++++ man/gemma4_quantize_nf4.Rd | 51 ++++ man/load_gemma4_nf4.Rd | 38 +++ man/load_gemma4_text_encoder.Rd | 4 +- tools/gen_fixtures_gemma4.py | 11 +- 9 files changed, 467 insertions(+), 5 deletions(-) create mode 100644 R/quantize_gemma4.R create mode 100644 inst/tinytest/test_gemma4_nf4.R create mode 100644 man/gemma4_quantize_nf4.Rd create mode 100644 man/load_gemma4_nf4.Rd diff --git a/NAMESPACE b/NAMESPACE index b19dc47..6265a7c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -72,6 +72,7 @@ export(gemma3_encode_batch) export(gemma3_quantize_nf4) export(gemma3_text_model) export(gemma3_tokenizer) +export(gemma4_quantize_nf4) export(gemma4_text_model) export(gemma4_tokenizer) export(img2img) @@ -82,6 +83,7 @@ export(load_decoder_weights) export(load_flux2_vae_decoder) export(load_gemma3_nf4) export(load_gemma3_text_encoder) +export(load_gemma4_nf4) export(load_gemma4_text_encoder) export(load_model_component) export(load_pipeline) diff --git a/R/gemma4_text_encoder.R b/R/gemma4_text_encoder.R index 3da20e1..4f12654 100644 --- a/R/gemma4_text_encoder.R +++ b/R/gemma4_text_encoder.R @@ -333,7 +333,9 @@ gemma4_text_model <- torch::nn_module( #' #' @param path Path to the LTX-2.5 text encoder file (e.g. #' \code{gemma4-12b-with-proj-ltx-2.5-bf16.safetensors}), which embeds -#' its config as \code{gemma_config} metadata. +#' its config as \code{gemma_config} metadata, or an NF4 artifact +#' directory from \code{\link{gemma4_quantize_nf4}} (dispatched to +#' \code{\link{load_gemma4_nf4}}). #' @param device Character. Device for the loaded model. #' @param dtype Character. "bfloat16" (checkpoint native) or "float32". #' @param pin Logical. When loading to the CPU, page-lock the weights so @@ -348,6 +350,10 @@ gemma4_text_model <- torch::nn_module( load_gemma4_text_encoder <- function(path, device = "cpu", dtype = "bfloat16", pin = getOption("diffuseR.pin_staging", TRUE), verbose = TRUE) { + if (file.exists(file.path(path, "manifest.json"))) { + return(load_gemma4_nf4(path, device = device, dtype = dtype, + pin = pin, verbose = verbose)) + } ckpt <- ltx23_open_checkpoint(path, require_version = NULL) meta <- ckpt$handle$metadata[["__metadata__"]] if (is.null(meta[["gemma_config"]])) { diff --git a/R/quantize_gemma4.R b/R/quantize_gemma4.R new file mode 100644 index 0000000..d06b84f --- /dev/null +++ b/R/quantize_gemma4.R @@ -0,0 +1,222 @@ +#' Quantize the LTX-2.5 Gemma 4 text encoder to NF4 shards +#' +#' Streams the LTX-2.5 encoder file tensor by tensor. The per-layer +#' projection weights (q/k/v/o and gate/up/down; 328 tensors in the 12B, +#' since full-attention layers have no v_proj) are stored as NF4 (packed +#' uint8 + \code{_absmax} float32 blocks); embeddings, norms and the +#' per-layer \code{layer_scalar} are copied at the resident dtype. Vision +#' and audio towers are dropped. +#' +#' The artifact is self-contained for the LTX-2.5 text side: it also +#' keeps the connectors' \code{text_embedding_projection.*} weights +#' (resident dtype, original keys) and writes the embedded tokenizer as +#' \code{tokenizer.json}, so the 26 GB source file is not needed after +#' quantizing. Load it with \code{\link{load_gemma4_nf4}} (or +#' \code{\link{load_gemma4_text_encoder}} on the directory) and the +#' tokenizer with \code{gemma4_tokenizer(output_dir)}. +#' +#' Peak host memory is about one shard (\code{shard_bytes}) plus the +#' largest single source tensor (the 2 GB bf16 embedding). +#' +#' @param path The LTX-2.5 text encoder file +#' (\code{gemma4-12b-with-proj-ltx-2.5-bf16.safetensors}). +#' @param output_dir Output directory (default: \code{gemma4-nf4} under +#' \code{tools::R_user_dir}). +#' @param shard_bytes Numeric. Target shard size in bytes; the 1.9e9 +#' default keeps shards readable by safetensors older than 0.3.0. +#' @param force Logical. Re-quantize even if a valid manifest exists. +#' @param verbose Logical. +#' +#' @return Invisibly, the manifest list. +#' +#' @export +gemma4_quantize_nf4 <- function(path, output_dir = NULL, shard_bytes = 1.9e9, + force = FALSE, verbose = TRUE) { + if (is.null(output_dir)) { + output_dir <- file.path(tools::R_user_dir("diffuseR", "data"), + "gemma4-nf4") + } + manifest_path <- file.path(output_dir, "manifest.json") + if (!force && file.exists(manifest_path)) { + manifest <- jsonlite::fromJSON(manifest_path) + if (identical(manifest$model, "gemma4") && + identical(manifest$format, "nf4") && + all(file.exists(file.path(output_dir, manifest$shards)))) { + if (verbose) { + message("NF4 artifact already present: ", output_dir) + } + return(invisible(manifest)) + } + } + + ckpt <- ltx23_open_checkpoint(path, require_version = NULL) + meta <- ckpt$handle$metadata[["__metadata__"]] + if (is.null(meta[["gemma_config"]])) { + stop("No gemma_config metadata in ", path, + "; expected an LTX-2.5 Gemma 4 text encoder file") + } + config_raw <- jsonlite::fromJSON(meta[["gemma_config"]], + simplifyVector = FALSE) + resident_dtype <- if (.st_can_write("bfloat16")) { + torch::torch_bfloat16() + } else { + message("safetensors cannot write bfloat16; storing resident ", + "tensors as float32 (larger artifact, same results)") + torch::torch_float32() + } + dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) + # A stale manifest must not validate a half-written artifact + unlink(manifest_path) + + if ("tokenizer_json" %in% ckpt$keys) { + writeLines(.gemma4_tokenizer_json(path), + file.path(output_dir, "tokenizer.json"), useBytes = TRUE) + } + + shard <- list() + shard_size <- 0 + shard_files <- character(0) + n_cast <- 0L + flush_shard <- function() { + if (!length(shard)) { + return() + } + fname <- sprintf("gemma4-nf4-%05d.safetensors", length(shard_files) + 1L) + safetensors::safe_save_file(shard, file.path(output_dir, fname)) + shard_files[[length(shard_files) + 1L]] <<- fname + if (verbose) { + message(sprintf(" wrote %s (%.2f GB, %d tensors)", fname, + shard_size / 1e9, length(shard))) + } + shard <<- list() + shard_size <<- 0 + gc(verbose = FALSE) + } + + for (key in ckpt$keys) { + name <- .gemma4_map_key(key) + if (is.na(name)) { + if (!startsWith(key, "text_embedding_projection.")) { + next + } + name <- key # kept for the LTX-2.5 connectors + } + tensor <- ckpt$handle$get_tensor(key) + if (.gemma3_is_quant_key(name)) { + torch::with_no_grad({ + q <- ltx23_nf4_quantize(tensor) + shard[[name]] <- q$packed + shard[[paste0(name, "_absmax")]] <- q$absmax + }) + shard_size <- shard_size + prod(tensor$shape) * 0.5625 + n_cast <- n_cast + 1L + } else { + shard[[name]] <- tensor$to(dtype = resident_dtype) + shard_size <- shard_size + prod(tensor$shape) * 2 + } + rm(tensor) + if (shard_size >= shard_bytes) { + flush_shard() + } + } + flush_shard() + + manifest <- list(model = "gemma4", format = "nf4", shards = shard_files, + n_cast = n_cast, config = config_raw) + jsonlite::write_json(manifest, manifest_path, auto_unbox = TRUE, + pretty = TRUE, digits = NA, null = "null") + if (verbose) { + message(sprintf("Quantized %d weights to NF4 across %d shards: %s", + n_cast, length(shard_files), output_dir)) + } + invisible(manifest) +} + +#' Load the LTX-2.5 Gemma 4 text encoder from an NF4 artifact +#' +#' Builds the model as a skeleton at the compute dtype, swaps the +#' projection linears for NF4 modules filled from the artifact +#' (dequantized per forward), copies the residents (parameters and the +#' \code{layer_scalar} buffers), and hard-errors on anything the artifact +#' does not fill. The connectors' projection weights in the artifact are +#' left for the connector loader. +#' +#' @param artifact_dir Directory produced by \code{\link{gemma4_quantize_nf4}}. +#' @param device "cuda" or "cpu". +#' @param dtype Compute dtype ("bfloat16" default, or "float32"). +#' @param pin Logical. When loading to the CPU, page-lock the weights so +#' \code{\link{encode_with_gemma4}} can move the model to the GPU per +#' encode at DMA speed. Default follows +#' \code{options(diffuseR.pin_staging)}. +#' @param verbose Logical. +#' +#' @return A \code{gemma4_text_model}. +#' +#' @export +load_gemma4_nf4 <- function(artifact_dir, device = "cuda", dtype = "bfloat16", + pin = getOption("diffuseR.pin_staging", TRUE), + verbose = TRUE) { + ckpt <- ltx23_open_fp8_checkpoint(artifact_dir) + manifest <- jsonlite::fromJSON(file.path(artifact_dir, "manifest.json"), + simplifyVector = FALSE) + if (!identical(manifest$model, "gemma4")) { + stop("Not a Gemma 4 NF4 artifact (manifest model is ", + manifest$model %||% "absent", "): ", artifact_dir) + } + config <- .gemma4_build_config(manifest$config) + torch_dtype <- switch(dtype, bfloat16 = torch::torch_bfloat16(), + float32 = torch::torch_float32(), + stop("Unsupported dtype: ", dtype)) + if (verbose) { + message(sprintf("Creating Gemma 4 NF4 model: %d layers, hidden_size=%d", + config$num_hidden_layers, config$hidden_size)) + } + model <- .construct_skeleton(gemma4_text_model, config, dtype = torch_dtype) + + cast_keys <- sub("_absmax$", "", grep("_absmax$", ckpt$keys, value = TRUE)) + torch::with_no_grad({ + for (key in cast_keys) { + # layers....weight + segs <- strsplit(sub("\\.weight$", "", key), ".", fixed = TRUE)[[1]] + parent <- model$layers[[as.integer(segs[2]) + 1L]][[segs[3]]] + old <- parent[[segs[4]]] + nf4 <- ltx23_nf4_linear(old$weight$shape[1], old$weight$shape[2], + bias = FALSE) + nf4$set_nf4_weight(ckpt$handle$get_tensor(key), + ckpt$handle$get_tensor(paste0(key, "_absmax"))) + do.call(`$<-`, list(parent, segs[4], nf4)) + } + + dests <- c(model$named_parameters(), model$named_buffers()) + dests <- dests[!grepl("weight_(nf4|absmax)$", names(dests))] + filled <- character(0) + residents <- setdiff(ckpt$keys, c(cast_keys, paste0(cast_keys, "_absmax"))) + for (key in residents) { + dest <- dests[[key]] + if (is.null(dest)) { + next # e.g. text_embedding_projection.*, for the connectors + } + dest$copy_(ckpt$handle$get_tensor(key)) + filled <- c(filled, key) + } + unfilled <- setdiff(names(dests), filled) + if (length(unfilled)) { + stop("Gemma 4 NF4 load: ", length(unfilled), + " parameters not filled, e.g. ", + paste(utils::head(unfilled, 3), collapse = ", ")) + } + }) + + model$to(device = device) + model$eval() + if (pin && device == "cpu") { + st <- .pin_component(model) + if (!is.null(st)) { + attr(model, "staging") <- st + if (verbose) { + message("Gemma 4 weights pinned for staged transfer") + } + } + } + model +} diff --git a/inst/tinytest/fixtures/gemma4_tiny_ckpt.safetensors b/inst/tinytest/fixtures/gemma4_tiny_ckpt.safetensors index 90f24ab26659fb4e1a94ff1c665874ed5f1b9fb9..7b59fab5dae4475c73736c3c16d698ca061408c8 100644 GIT binary patch delta 5876 zcmb`L&2Hn!6@`(`yg>2<0vDYui~+0oZx{`T615A^NoSCS(GX~fZYv{;iYRq^Vi-h_ zMcyE*yhK)cm01kXKwcp0Ae+=Z_nvmQ5%dJfEDZDVr|Nvgs*e=E{l`!K^_QPM`}xoQ z`s};szdVbM{ny=Y^ZV|w{O!>1qVxM`ef(zMMd#61Wpow|H?0(xS>dwRXVJPnw#)wd zdgzWr*y~GQ7XHkaWmXoiA08%?KfZtdr}xi)6#kxtzaRhY{qryW`EtCE-gbv!BkfNj z_hlTNO`_xBv|F`D-MKq$w_)GDU9UI0H{mSX5$bNeM8?qH`ER9quDtB9rJ>-G1AyI+=U>_IT_LGQ;nqS7E+k8b^~T z%$xD!$@=#{|3UUe`$MM_TNd9ZjlSA%H^=X3GP>$++P9ltu8e-s9o}^7-)xRIFArVV zc^c&E%`PlDSs&MMq)i)GJgIVSy=y}@%q!ooi=WZ^3e(R4??!?R{a{| zXy&*Lr7T?`97SQ1sD9Z*9PO>*XT88{_yzXeiSvc2eY4 zcqs}i5-Cb6QYk7cGAW#RH?$Y$s1PAk1uBFHRf!5QLRCpMItvm{agaf3R7^5M#VB)B z%+jD@n1xi{!Z@&b3*!LiEsO)5A1#dopSLg$fZoD55PA#aKF#};~Lz989 zv@jV6OAC{Mu(B{22rCPdfv~bL83-#2lL4?|7ywobGlHOg9%wOgR@yOgk4!Og$G$Og|U6zm9?~<}XHhc2UuV64TLz5+^oX zr4m!pr4rN9r4m!qr4rNAr4m!rr2+?2tA9%+rmIUOrmRaP4z|X+FO`_KE|r+NE|r+R zE|r+VF7+1E*kv=~=}BjoN=#{2N=$24N-WwIYgbB4ZC6T6Z&ym3*-fmJnC7k&ILqw$ zS1B>&T`4i`T`4j3T`4jAEjg5!{;rgm0w+-C8o!x zN~|J}@1{yjkx!MFCZ8&CVK;HAz{O~jYpTSw`BaIi^QjWk=Tjx7(5FglM!%aXF^xV| zVk&*Az;yapD>1EJD>1cRD>1!ZD>21hD>2PpD>2nxD>2nxD>2<(EAU9W*Gf#i*Gf#k z*Gf#m*Gf#o*Gf#q*Gf#s*Gf#s&lK1sKiG>ImJv~1Iv8U%}Aole948)$EpMluZ^D_{8dVUUKPtDH( z>{avO{bLSdPtVVln4X_2F+D$5VtRhA#Ps}JiRt;d67vOo4q{Ku&jom#czS-W#Ps}J ziRt;d64Uc@C8p=+N=(l;N=(l;N=(f+3JlH5^S4oAdcIL&dcIL&dcIL&dcIL&dcIL& zdcIL&zL7V2i>di00Eg$Vr{^0brso?arso?arso?arso$*OwTWrn4VuKF*UzXV9`9D zzY8U%=NC#$&o7jio?j?2J-<+5dVZnA^!!4J`HH^KTTIO_W_bR3dVZnA^!!4J>G_2c z!}G2D%7fzNXRwFeSJ;y0jeM!|P`doM_ptd5w&dkygDrV)2_FH%{DZ%5u~S}G!du;rVD$9uQ@3iq(Id?oM@^gC?HYton4l1}7PjvSY6!nU3)FGn|s1RKl#QETXU zD<361Ox)SFJRN$p4+V!SKnVd$fpLI;TXX^U;((UcJyOHG;p>s~A)w*3VKbtJbwdqe zICmE+IkWQe3FX5Fh|5cmgCvGuPvla_absmax} float32 blocks); embeddings, norms and the +per-layer \code{layer_scalar} are copied at the resident dtype. Vision +and audio towers are dropped. +} +\details{ +The artifact is self-contained for the LTX-2.5 text side: it also +keeps the connectors' \code{text_embedding_projection.*} weights +(resident dtype, original keys) and writes the embedded tokenizer as +\code{tokenizer.json}, so the 26 GB source file is not needed after +quantizing. Load it with \code{\link{load_gemma4_nf4}} (or +\code{\link{load_gemma4_text_encoder}} on the directory) and the +tokenizer with \code{gemma4_tokenizer(output_dir)}. + +Peak host memory is about one shard (\code{shard_bytes}) plus the +largest single source tensor (the 2 GB bf16 embedding). + +} diff --git a/man/load_gemma4_nf4.Rd b/man/load_gemma4_nf4.Rd new file mode 100644 index 0000000..abb90fa --- /dev/null +++ b/man/load_gemma4_nf4.Rd @@ -0,0 +1,38 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{load_gemma4_nf4} +\alias{load_gemma4_nf4} +\title{Load the LTX-2.5 Gemma 4 text encoder from an NF4 artifact} +\usage{ +load_gemma4_nf4( + artifact_dir, + device = "cuda", + dtype = "bfloat16", + pin = getOption("diffuseR.pin_staging", TRUE), + verbose = TRUE +) +} +\arguments{ +\item{artifact_dir}{Directory produced by \code{\link{gemma4_quantize_nf4}}.} + +\item{device}{"cuda" or "cpu".} + +\item{dtype}{Compute dtype ("bfloat16" default, or "float32").} + +\item{pin}{Logical. When loading to the CPU, page-lock the weights so +\code{\link{encode_with_gemma4}} can move the model to the GPU per +encode at DMA speed. Default follows +\code{options(diffuseR.pin_staging)}.} + +\item{verbose}{Logical.} +} +\value{ +A \code{gemma4_text_model}. +} +\description{ +Builds the model as a skeleton at the compute dtype, swaps the +projection linears for NF4 modules filled from the artifact +(dequantized per forward), copies the residents (parameters and the +\code{layer_scalar} buffers), and hard-errors on anything the artifact +does not fill. The connectors' projection weights in the artifact are +left for the connector loader. +} diff --git a/man/load_gemma4_text_encoder.Rd b/man/load_gemma4_text_encoder.Rd index 5c7c9db..2389aa2 100644 --- a/man/load_gemma4_text_encoder.Rd +++ b/man/load_gemma4_text_encoder.Rd @@ -14,7 +14,9 @@ load_gemma4_text_encoder( \arguments{ \item{path}{Path to the LTX-2.5 text encoder file (e.g. \code{gemma4-12b-with-proj-ltx-2.5-bf16.safetensors}), which embeds -its config as \code{gemma_config} metadata.} +its config as \code{gemma_config} metadata, or an NF4 artifact +directory from \code{\link{gemma4_quantize_nf4}} (dispatched to +\code{\link{load_gemma4_nf4}}).} \item{device}{Character. Device for the loaded model.} diff --git a/tools/gen_fixtures_gemma4.py b/tools/gen_fixtures_gemma4.py index 37d9dcb..71e99f6 100644 --- a/tools/gen_fixtures_gemma4.py +++ b/tools/gen_fixtures_gemma4.py @@ -105,9 +105,6 @@ gemma_config = {"architectures": ["Gemma4UnifiedForConditionalGeneration"], "gemma_version": "gemma4-12b-ltx-v1", "text_config": text_config} -save_file(ckpt, os.path.join(OUT_DIR, "gemma4_tiny_ckpt.safetensors"), - metadata={"gemma_config": json.dumps(gemma_config), "format": "pt"}) -print(f"wrote {len(ckpt)} tensors to {OUT_DIR}/gemma4_tiny_ckpt.safetensors") # --- Tokenizer ------------------------------------------------------------------ @@ -155,6 +152,14 @@ with open(tok_path, "w", encoding="utf-8") as f: json.dump(tok_json, f, ensure_ascii=False) +# The LTX file carries its tokenizer.json as a uint8 tensor +with open(tok_path, "rb") as f: + tok_bytes = f.read() +ckpt["tokenizer_json"] = torch.tensor(list(tok_bytes), dtype=torch.uint8) +save_file(ckpt, os.path.join(OUT_DIR, "gemma4_tiny_ckpt.safetensors"), + metadata={"gemma_config": json.dumps(gemma_config), "format": "pt"}) +print(f"wrote {len(ckpt)} tensors to {OUT_DIR}/gemma4_tiny_ckpt.safetensors") + tok = Tokenizer.from_file(tok_path) prompts = ["the cat sat", "hello cat", " the cat ", "tell", "caté ok", "thecat", "sit on the mat."] From 91173ad402e5fe7787ed3c0410ece8479779be07 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 13:12:04 -0500 Subject: [PATCH 07/14] Keep Gemma 4 NF4 shards under the bound; require the tokenizer on reuse - Flush before adding a tensor that would push the shard past shard_bytes (an oversized single tensor, like the 2 GB embedding, gets its own shard), and count residents at their stored element size, so the float32 fallback is sized correctly. - The manifest records tokenizer.json; reuse requires it along with the shards, so an artifact missing its tokenizer is rebuilt. --- R/quantize_gemma4.R | 24 +++++++++++++++++------- inst/tinytest/test_gemma4_nf4.R | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/R/quantize_gemma4.R b/R/quantize_gemma4.R index d06b84f..0b799dd 100644 --- a/R/quantize_gemma4.R +++ b/R/quantize_gemma4.R @@ -39,9 +39,12 @@ gemma4_quantize_nf4 <- function(path, output_dir = NULL, shard_bytes = 1.9e9, manifest_path <- file.path(output_dir, "manifest.json") if (!force && file.exists(manifest_path)) { manifest <- jsonlite::fromJSON(manifest_path) + # Complete means every shard plus the tokenizer the manifest + # promises; anything less is rebuilt if (identical(manifest$model, "gemma4") && identical(manifest$format, "nf4") && - all(file.exists(file.path(output_dir, manifest$shards)))) { + all(file.exists(file.path(output_dir, + c(manifest$shards, manifest$tokenizer))))) { if (verbose) { message("NF4 artifact already present: ", output_dir) } @@ -64,6 +67,7 @@ gemma4_quantize_nf4 <- function(path, output_dir = NULL, shard_bytes = 1.9e9, "tensors as float32 (larger artifact, same results)") torch::torch_float32() } + resident_bytes <- torch::torch_empty(1L, dtype = resident_dtype)$element_size() dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) # A stale manifest must not validate a half-written artifact unlink(manifest_path) @@ -102,27 +106,33 @@ gemma4_quantize_nf4 <- function(path, output_dir = NULL, shard_bytes = 1.9e9, name <- key # kept for the LTX-2.5 connectors } tensor <- ckpt$handle$get_tensor(key) - if (.gemma3_is_quant_key(name)) { + quant <- .gemma3_is_quant_key(name) + bytes <- prod(tensor$shape) * if (quant) 0.5625 else resident_bytes + # Flush first so a shard stays under shard_bytes; a tensor larger + # than the target alone (the 2 GB embedding) gets its own shard + if (shard_size + bytes > shard_bytes) { + flush_shard() + } + if (quant) { torch::with_no_grad({ q <- ltx23_nf4_quantize(tensor) shard[[name]] <- q$packed shard[[paste0(name, "_absmax")]] <- q$absmax }) - shard_size <- shard_size + prod(tensor$shape) * 0.5625 n_cast <- n_cast + 1L } else { shard[[name]] <- tensor$to(dtype = resident_dtype) - shard_size <- shard_size + prod(tensor$shape) * 2 } + shard_size <- shard_size + bytes rm(tensor) - if (shard_size >= shard_bytes) { - flush_shard() - } } flush_shard() manifest <- list(model = "gemma4", format = "nf4", shards = shard_files, n_cast = n_cast, config = config_raw) + if (file.exists(file.path(output_dir, "tokenizer.json"))) { + manifest$tokenizer <- "tokenizer.json" + } jsonlite::write_json(manifest, manifest_path, auto_unbox = TRUE, pretty = TRUE, digits = NA, null = "null") if (verbose) { diff --git a/inst/tinytest/test_gemma4_nf4.R b/inst/tinytest/test_gemma4_nf4.R index e443e25..aa1357b 100644 --- a/inst/tinytest/test_gemma4_nf4.R +++ b/inst/tinytest/test_gemma4_nf4.R @@ -52,6 +52,28 @@ art_seen <- local({ # A second call reuses the artifact expect_identical(gemma4_quantize_nf4(src, art, verbose = FALSE)$shards, manifest$shards) + # ...but only a complete one: a missing tokenizer.json forces a rebuild + expect_equal(manifest$tokenizer, "tokenizer.json") + unlink(file.path(art, "tokenizer.json")) + expect_message(gemma4_quantize_nf4(src, art, verbose = TRUE), "Quantized") + expect_true(file.exists(file.path(art, "tokenizer.json"))) + + # Shards stay under shard_bytes; only a single oversized source tensor + # (NF4 packed + absmax pair, or one resident) may exceed it alone + local({ + small <- tempfile("gemma4-nf4-small-") + on.exit(unlink(small, recursive = TRUE), add = TRUE) + bound <- 20000 + m <- gemma4_quantize_nf4(src, small, shard_bytes = bound, verbose = FALSE) + expect_true(length(m$shards) > 1L) + for (s in m$shards) { + w <- safetensors::safe_load_file(file.path(small, s), framework = "torch") + payload <- sum(vapply(w, function(t) t$numel() * t$element_size(), 0)) + n_src <- length(setdiff(names(w), grep("_absmax$", names(w), value = TRUE))) + expect_true(payload <= bound * 1.01 || n_src == 1L, + info = sprintf("%s: %.0f bytes, %d tensors", s, payload, n_src)) + } + }) enc <- load_gemma4_text_encoder(art, device = "cpu", dtype = "float32", pin = FALSE, verbose = FALSE) From 2f6584d190501b9d424591515e0e6a1a06bba8e3 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 13:38:02 -0500 Subject: [PATCH 08/14] Collect garbage per tensor while quantizing Gemma 4 R's gc cannot see torch's native memory, so the dropped source tensors and NF4 temporaries accumulated between shard flushes: the real 12B run was OOM-killed at a 20 GB cgroup cap within 27 s. With a gc per tensor it completes in 110 s at 6.86 GB peak RSS (328 casts, 6 shards, 10.46 GB artifact). --- R/quantize_gemma4.R | 8 +++++++- man/gemma4_quantize_nf4.Rd | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/R/quantize_gemma4.R b/R/quantize_gemma4.R index 0b799dd..4d5851d 100644 --- a/R/quantize_gemma4.R +++ b/R/quantize_gemma4.R @@ -16,7 +16,8 @@ #' tokenizer with \code{gemma4_tokenizer(output_dir)}. #' #' Peak host memory is about one shard (\code{shard_bytes}) plus the -#' largest single source tensor (the 2 GB bf16 embedding). +#' largest single source tensor and its save buffer (the 2 GB bf16 +#' embedding); measured under a 20 GB cgroup cap on the 12B. #' #' @param path The LTX-2.5 text encoder file #' (\code{gemma4-12b-with-proj-ltx-2.5-bf16.safetensors}). @@ -125,6 +126,11 @@ gemma4_quantize_nf4 <- function(path, output_dir = NULL, shard_bytes = 1.9e9, } shard_size <- shard_size + bytes rm(tensor) + # R's gc cannot see torch's native memory: without a collection + # per tensor the dropped source tensors and quantization + # temporaries (up to ~0.24 GB each) pile up past 20 GB within + # seconds on the 12B (measured: cgroup OOM kill) + gc(verbose = FALSE) } flush_shard() diff --git a/man/gemma4_quantize_nf4.Rd b/man/gemma4_quantize_nf4.Rd index b086f04..3ef285e 100644 --- a/man/gemma4_quantize_nf4.Rd +++ b/man/gemma4_quantize_nf4.Rd @@ -46,6 +46,7 @@ quantizing. Load it with \code{\link{load_gemma4_nf4}} (or tokenizer with \code{gemma4_tokenizer(output_dir)}. Peak host memory is about one shard (\code{shard_bytes}) plus the -largest single source tensor (the 2 GB bf16 embedding). +largest single source tensor and its save buffer (the 2 GB bf16 +embedding); measured under a 20 GB cgroup cap on the 12B. } From 199904c0dd0c6814d502690ba3ad1f48e26408dc Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 14:09:17 -0500 Subject: [PATCH 09/14] Load the split LTX-2.5 files through the 2.3 pipeline LTX-2.5 ships its components as separate files, and the connectors' text projection lives in the Gemma 4 encoder file rather than with the transformer. ltx25_open_checkpoint() presents the transformer (single file or fp8/NF4 artifact), the projection (from the encoder file or its NF4 artifact), the convolutional video VAE (keys gain their vae. prefix) and the audio VAE + vocoder as one ltx23_checkpoint under the 2.3 key names, routing each get_tensor() to its source file. It refuses a non-2.5 transformer, the diffusion-decoder VAE, and an encoder file without the projection. - ltx23_load_pipeline() accepts an open ltx23_checkpoint. - txt2vid_ltx2() encodes with Gemma 4 when given a gemma4_tokenizer. - An encoder already loaded on the GPU encodes there instead of being sent to the CPU path. - The LTX fp8/NF4 quantizers collect garbage per tensor, as the Gemma 4 quantizer now does (R's gc cannot see torch's native memory). --- NAMESPACE | 1 + R/checkpoint_ltx25.R | 102 ++++++++++++++++++++++++++ R/fp8_ltx23.R | 10 +-- R/nf4_ltx23.R | 12 +-- R/txt2vid_ltx23.R | 37 ++++++++-- inst/tinytest/test_checkpoint_ltx25.R | 98 +++++++++++++++++++++++++ man/ltx23_load_pipeline.Rd | 4 +- man/ltx25_open_checkpoint.Rd | 36 +++++++++ man/txt2vid_ltx2.Rd | 5 +- 9 files changed, 284 insertions(+), 21 deletions(-) create mode 100644 R/checkpoint_ltx25.R create mode 100644 inst/tinytest/test_checkpoint_ltx25.R create mode 100644 man/ltx25_open_checkpoint.Rd diff --git a/NAMESPACE b/NAMESPACE index 6265a7c..439c73e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -180,6 +180,7 @@ export(ltx23_video_vae) export(ltx23_vocoder) export(ltx23_vocoder_resblock) export(ltx23_vocoder_with_bwe) +export(ltx25_open_checkpoint) export(models2devices) export(offload_to_cpu) export(post_quant_conv) diff --git a/R/checkpoint_ltx25.R b/R/checkpoint_ltx25.R new file mode 100644 index 0000000..bf8995d --- /dev/null +++ b/R/checkpoint_ltx25.R @@ -0,0 +1,102 @@ +#' Open the LTX-2.5 component files as one checkpoint +#' +#' LTX-2.3 shipped every component in one file; LTX-2.5 splits them: the +#' transformer (with the text connectors), the Gemma 4 text encoder file +#' (which carries the connectors' \code{text_embedding_projection}), the +#' video VAE, and the audio VAE plus vocoder. This presents them as a +#' single \code{ltx23_checkpoint} under the 2.3 key names (the video VAE +#' keys gain their \code{vae.} prefix), so \code{\link{ltx23_load_pipeline}} +#' and its component loaders work unchanged. +#' +#' @param transformer The 2.5 transformer file +#' (\code{ltx-2.5-22b-distilled-transformer-bf16.safetensors}) or an +#' fp8/NF4 artifact directory quantized from it. +#' @param text_encoder The 2.5 Gemma 4 text encoder file, or its NF4 +#' artifact directory (\code{\link{gemma4_quantize_nf4}} keeps the +#' projection); only \code{text_embedding_projection.*} is used here. +#' @param video_vae The 2.5 video VAE file. Use the convolutional decoder +#' (\code{ltx-2.5-video-vae-conv-bf16.safetensors}); the default +#' diffusion decoder is not supported. +#' @param audio_vae Optional 2.5 audio VAE + vocoder file +#' (\code{ltx-2.5-audio-vae-bf16.safetensors}). +#' +#' @return An \code{ltx23_checkpoint} whose \code{handle$get_tensor()} +#' reads each key from its source file. +#' +#' @export +ltx25_open_checkpoint <- function(transformer, text_encoder, video_vae, + audio_vae = NULL) { + open_any <- function(path, require_version) { + if (file.exists(file.path(path, "manifest.json"))) { + ltx23_open_fp8_checkpoint(path) + } else { + ltx23_open_checkpoint(path, require_version = require_version) + } + } + dit <- open_any(transformer, "2.5") + if (!startsWith(dit$version %||% "", "2.5")) { + stop("Transformer is model_version '", dit$version %||% "none", + "', expected 2.5*: ", transformer) + } + te <- open_any(text_encoder, NULL) + vae <- ltx23_open_checkpoint(video_vae, require_version = "2.5") + if (any(grepl("(^|\\.)transformer_blocks\\.", vae$keys)) || + !any(grepl("^(vae\\.)?decoder\\.up_blocks\\.", vae$keys))) { + stop("Not a convolutional video VAE file (the diffusion decoder is ", + "not supported): ", video_vae) + } + sources <- list(dit = dit, te = te, vae = vae) + if (!is.null(audio_vae)) { + sources$audio <- ltx23_open_checkpoint(audio_vae, require_version = "2.5") + } + + # canonical key -> (source, original key) + proj <- grep("^text_embedding_projection\\.", te$keys, value = TRUE) + if (!length(proj)) { + stop("No text_embedding_projection.* in ", text_encoder) + } + vae_keys <- vae$keys + routes <- list( + dit = list(orig = dit$keys, canon = dit$keys), + te = list(orig = proj, canon = proj), + vae = list(orig = vae_keys, + canon = ifelse(startsWith(vae_keys, "vae."), vae_keys, + paste0("vae.", vae_keys))) + ) + if (!is.null(sources$audio)) { + ak <- grep("^(audio_vae|vocoder)\\.", sources$audio$keys, value = TRUE) + routes$audio <- list(orig = ak, canon = ak) + } + canon <- unlist(lapply(routes, `[[`, "canon"), use.names = FALSE) + orig <- unlist(lapply(routes, `[[`, "orig"), use.names = FALSE) + src <- rep(names(routes), vapply(routes, function(r) length(r$canon), 1L)) + dup <- canon[duplicated(canon)] + if (length(dup)) { + stop("Key present in more than one LTX-2.5 file: ", dup[1]) + } + + handle <- list(get_tensor = function(key) { + i <- match(key, canon) + if (is.na(i)) { + stop("Key not found in the LTX-2.5 files: ", key) + } + sources[[src[i]]]$handle$get_tensor(orig[i]) + }) + + vcfg <- vae$config + acfg <- sources$audio$config + config <- list(transformer = dit$config$transformer, + scheduler = dit$config$scheduler, + vae = vcfg$vae %||% vcfg, + audio_vae = acfg$audio_vae, vocoder = acfg$vocoder) + structure( + list(handle = handle, keys = canon, version = dit$version, + config = config, format = dit$format, path = transformer, + sources = vapply(list(transformer = transformer, + text_encoder = text_encoder, + video_vae = video_vae, + audio_vae = audio_vae %||% NA_character_), + path.expand, "")), + class = "ltx23_checkpoint" + ) +} diff --git a/R/fp8_ltx23.R b/R/fp8_ltx23.R index c6cb67b..aeb737e 100644 --- a/R/fp8_ltx23.R +++ b/R/fp8_ltx23.R @@ -193,11 +193,11 @@ ltx23_quantize_fp8 <- function(checkpoint_path, output_dir = NULL, if (shard_size >= shard_bytes) { flush_shard() } - if (i %% 200L == 0L) { - gc(verbose = FALSE) - if (verbose) { - message(sprintf(" quantizing %d/%d tensors", i, length(keys))) - } + # Per tensor, as in ltx23_quantize_nf4(): R's gc cannot see + # torch's native memory + gc(verbose = FALSE) + if (i %% 200L == 0L && verbose) { + message(sprintf(" quantizing %d/%d tensors", i, length(keys))) } } flush_shard() diff --git a/R/nf4_ltx23.R b/R/nf4_ltx23.R index a582d4f..973e315 100644 --- a/R/nf4_ltx23.R +++ b/R/nf4_ltx23.R @@ -337,11 +337,13 @@ ltx23_quantize_nf4 <- function(checkpoint_path, output_dir = NULL, if (shard_size >= shard_bytes) { flush_shard() } - if (i %% 200L == 0L) { - gc(verbose = FALSE) - if (verbose) { - message(sprintf(" quantizing %d/%d tensors", i, length(keys))) - } + # Per tensor: R's gc cannot see torch's native memory, and 200 + # dropped bf16 sources plus float32 quantization temporaries + # (~0.27 GB each for the FFN weights) outgrow the free RAM of a + # host sharing memory with other services (cf. gemma4_quantize_nf4) + gc(verbose = FALSE) + if (i %% 200L == 0L && verbose) { + message(sprintf(" quantizing %d/%d tensors", i, length(keys))) } } flush_shard() diff --git a/R/txt2vid_ltx23.R b/R/txt2vid_ltx23.R index e1f17c8..4a17f74 100644 --- a/R/txt2vid_ltx23.R +++ b/R/txt2vid_ltx23.R @@ -247,7 +247,9 @@ ltx23_unpack_audio_latents <- function(latents, num_mel_bins) { #' \code{ltx-2.3-22b-distilled-1.1.safetensors}) or to an fp8 artifact #' directory produced by \code{\link{ltx23_quantize_fp8}}. With the fp8 #' artifact, the transformer loads with CPU-resident fp8 weights that -#' stream to \code{device} during the forward pass. +#' stream to \code{device} during the forward pass. An open +#' \code{ltx23_checkpoint} is also accepted, such as the LTX-2.5 +#' component set from \code{\link{ltx25_open_checkpoint}}. #' @param device Character. Device for the small components (VAEs, #' vocoder, connectors) and, with fp8, the transformer residents. #' @param dtype Character. "bfloat16" (checkpoint native) or "float32". @@ -277,11 +279,17 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", } else { component_device <- device } - fp8 <- dir.exists(checkpoint_path) - ckpt <- if (fp8) { - ltx23_open_fp8_checkpoint(checkpoint_path) + if (inherits(checkpoint_path, "ltx23_checkpoint")) { + # An already-open checkpoint, e.g. ltx25_open_checkpoint() + ckpt <- checkpoint_path + fp8 <- !is.null(ckpt$format) } else { - ltx23_open_checkpoint(checkpoint_path) + fp8 <- dir.exists(checkpoint_path) + ckpt <- if (fp8) { + ltx23_open_fp8_checkpoint(checkpoint_path) + } else { + ltx23_open_checkpoint(checkpoint_path) + } } if (!nzchar(Sys.getenv("PYTORCH_CUDA_ALLOC_CONF"))) { # Must be set before the first CUDA allocation. NF4 runs the @@ -449,6 +457,12 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", if (!is.null(attr(text_encoder, "staging")) && grepl("^cuda", device)) { return(device) } + # An encoder already loaded on the GPU encodes there + on_gpu <- tryCatch(text_encoder$parameters[[1]]$device$type == "cuda", + error = function(e) FALSE) + if (isTRUE(on_gpu) && grepl("^cuda", device)) { + return(device) + } "cpu" } @@ -465,8 +479,9 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", #' \code{\link{ltx23_load_pipeline}}. #' @param text_encoder,tokenizer Gemma3 model and tokenizer (or paths; #' see \code{\link{load_gemma3_text_encoder}} and -#' \code{\link{gemma3_tokenizer}}). Ignored when \code{prompt_embeds} -#' is supplied. +#' \code{\link{gemma3_tokenizer}}), or for LTX-2.5 the Gemma 4 model +#' and \code{\link{gemma4_tokenizer}} (selected by the tokenizer's +#' class). Ignored when \code{prompt_embeds} is supplied. #' @param prompt_embeds Optional precomputed list with #' \code{prompt_embeds} (raw stacked Gemma3 states) and #' \code{prompt_attention_mask}; bypasses the text encoder. @@ -644,7 +659,13 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, if (verbose) { message("Encoding prompt...") } - prompt_embeds <- encode_with_gemma3( + # LTX-2.5 encodes with Gemma 4 (chosen by its tokenizer class) + encode <- if (inherits(tokenizer, "gemma4_tokenizer")) { + encode_with_gemma4 + } else { + encode_with_gemma3 + } + prompt_embeds <- encode( prompt, model = text_encoder, tokenizer = tokenizer, max_sequence_length = max_sequence_length, diff --git a/inst/tinytest/test_checkpoint_ltx25.R b/inst/tinytest/test_checkpoint_ltx25.R new file mode 100644 index 0000000..fdb3f8d --- /dev/null +++ b/inst/tinytest/test_checkpoint_ltx25.R @@ -0,0 +1,98 @@ +# ltx25_open_checkpoint(): the split LTX-2.5 files presented as one +# ltx23_checkpoint under the 2.3 key names. + +if (!requireNamespace("torch", quietly = TRUE) || !torch::torch_is_installed()) { + exit_file("torch not fully installed") +} +if (!requireNamespace("safetensors", quietly = TRUE)) { + exit_file("safetensors not installed") +} + +library(diffuseR) + +local({ + dir <- tempfile("ltx25-ckpt-") + dir.create(dir) + on.exit(unlink(dir, recursive = TRUE), add = TRUE) + path <- function(n) file.path(dir, n) + save <- function(tensors, name, version = "2.5.0", config = NULL) { + meta <- list(model_version = version) + if (!is.null(config)) { + meta$config <- as.character(jsonlite::toJSON(config, auto_unbox = TRUE)) + } + if (is.na(version)) meta$model_version <- NULL + safetensors::safe_save_file(tensors, path(name), metadata = meta) + } + t <- function(v) torch::torch_tensor(v) + + save(list("model.diffusion_model.patchify_proj.weight" = t(c(1, 2)), + "model.diffusion_model.video_embeddings_connector.learnable_registers" = t(3)), + "dit.safetensors", + config = list(transformer = list(ff_bias = FALSE), scheduler = list(a = 1))) + save(list("model.layers.0.mlp.up_proj.weight" = t(9), + "text_embedding_projection.video_aggregate_embed.weight" = t(c(4, 5))), + "te.safetensors", version = NA) + save(list("decoder.up_blocks.0.conv.weight" = t(6), + "per_channel_statistics.mean-of-means" = t(7)), + "vae.safetensors", config = list(vae = list(latent_channels = 128L))) + save(list("audio_vae.decoder.conv_in.weight" = t(8), + "vocoder.vocoder.conv_in.weight" = t(10)), + "audio.safetensors", + config = list(audio_vae = list(x = 1), vocoder = list(y = 2))) + + ck <- ltx25_open_checkpoint(path("dit.safetensors"), path("te.safetensors"), + path("vae.safetensors"), path("audio.safetensors")) + expect_inherits(ck, "ltx23_checkpoint") + expect_equal(ck$version, "2.5.0") + expect_null(ck$format) + + groups <- ltx23_split_keys(ck$keys) + expect_equal(groups$dit, "model.diffusion_model.patchify_proj.weight") + expect_equal(sort(groups$connectors), + sort(c("model.diffusion_model.video_embeddings_connector.learnable_registers", + "text_embedding_projection.video_aggregate_embed.weight"))) + expect_equal(sort(groups$vae), sort(c("vae.decoder.up_blocks.0.conv.weight", + "vae.per_channel_statistics.mean-of-means"))) + expect_equal(groups$audio_vae, "audio_vae.decoder.conv_in.weight") + expect_equal(groups$vocoder, "vocoder.vocoder.conv_in.weight") + expect_equal(length(groups$other), 0L) + # The encoder's own weights are not part of the pipeline checkpoint + expect_false(any(grepl("^model\\.layers", ck$keys))) + + # Each key reads from its own file + get <- function(k) as.numeric(ck$handle$get_tensor(k)) + expect_equal(get("text_embedding_projection.video_aggregate_embed.weight"), c(4, 5)) + expect_equal(get("vae.per_channel_statistics.mean-of-means"), 7) + expect_equal(get("vocoder.vocoder.conv_in.weight"), 10) + expect_equal(get("model.diffusion_model.patchify_proj.weight"), c(1, 2)) + expect_error(ck$handle$get_tensor("nope"), "not found") + # The VAE map strips the prefix the composite added + expect_equal(ltx23_map_vae_key("vae.per_channel_statistics.mean-of-means"), + "latents_mean") + + # Configs merge from their own files + expect_false(ck$config$transformer$ff_bias) + expect_equal(ck$config$vae$latent_channels, 128L) + expect_equal(ck$config$vocoder$y, 2L) + + # Audio is optional + ck2 <- ltx25_open_checkpoint(path("dit.safetensors"), path("te.safetensors"), + path("vae.safetensors")) + expect_equal(length(ltx23_split_keys(ck2$keys)$vocoder), 0L) + + # Wrong files are refused + save(list("model.diffusion_model.patchify_proj.weight" = t(1)), "dit23.safetensors", + version = "2.3.0") + expect_error(ltx25_open_checkpoint(path("dit23.safetensors"), path("te.safetensors"), + path("vae.safetensors")), "2\\.5") + save(list("decoder.transformer_blocks.0.w" = t(1)), "vaediff.safetensors") + expect_error(ltx25_open_checkpoint(path("dit.safetensors"), path("te.safetensors"), + path("vaediff.safetensors")), "convolutional") + expect_error(ltx25_open_checkpoint(path("dit.safetensors"), path("vae.safetensors"), + path("vae.safetensors")), "text_embedding_projection") +}) + +# An encoder already on the CPU encodes there; staging or the GPU decides otherwise +m <- torch::nn_linear(2, 2) +expect_equal(diffuseR:::.ltx23_text_encode_device(m, "cuda"), "cpu") +expect_equal(diffuseR:::.ltx23_text_encode_device("path", "cuda"), "cuda") diff --git a/man/ltx23_load_pipeline.Rd b/man/ltx23_load_pipeline.Rd index e86f851..c982fdb 100644 --- a/man/ltx23_load_pipeline.Rd +++ b/man/ltx23_load_pipeline.Rd @@ -20,7 +20,9 @@ ltx23_load_pipeline( \code{ltx-2.3-22b-distilled-1.1.safetensors}) or to an fp8 artifact directory produced by \code{\link{ltx23_quantize_fp8}}. With the fp8 artifact, the transformer loads with CPU-resident fp8 weights that -stream to \code{device} during the forward pass.} +stream to \code{device} during the forward pass. An open +\code{ltx23_checkpoint} is also accepted, such as the LTX-2.5 +component set from \code{\link{ltx25_open_checkpoint}}.} \item{device}{Character. Device for the small components (VAEs, vocoder, connectors) and, with fp8, the transformer residents.} diff --git a/man/ltx25_open_checkpoint.Rd b/man/ltx25_open_checkpoint.Rd new file mode 100644 index 0000000..56b17e5 --- /dev/null +++ b/man/ltx25_open_checkpoint.Rd @@ -0,0 +1,36 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{ltx25_open_checkpoint} +\alias{ltx25_open_checkpoint} +\title{Open the LTX-2.5 component files as one checkpoint} +\usage{ +ltx25_open_checkpoint(transformer, text_encoder, video_vae, audio_vae = NULL) +} +\arguments{ +\item{transformer}{The 2.5 transformer file +(\code{ltx-2.5-22b-distilled-transformer-bf16.safetensors}) or an +fp8/NF4 artifact directory quantized from it.} + +\item{text_encoder}{The 2.5 Gemma 4 text encoder file, or its NF4 +artifact directory (\code{\link{gemma4_quantize_nf4}} keeps the +projection); only \code{text_embedding_projection.*} is used here.} + +\item{video_vae}{The 2.5 video VAE file. Use the convolutional decoder +(\code{ltx-2.5-video-vae-conv-bf16.safetensors}); the default +diffusion decoder is not supported.} + +\item{audio_vae}{Optional 2.5 audio VAE + vocoder file +(\code{ltx-2.5-audio-vae-bf16.safetensors}).} +} +\value{ +An \code{ltx23_checkpoint} whose \code{handle$get_tensor()} + reads each key from its source file. +} +\description{ +LTX-2.3 shipped every component in one file; LTX-2.5 splits them: the +transformer (with the text connectors), the Gemma 4 text encoder file +(which carries the connectors' \code{text_embedding_projection}), the +video VAE, and the audio VAE plus vocoder. This presents them as a +single \code{ltx23_checkpoint} under the 2.3 key names (the video VAE +keys gain their \code{vae.} prefix), so \code{\link{ltx23_load_pipeline}} +and its component loaders work unchanged. +} diff --git a/man/txt2vid_ltx2.Rd b/man/txt2vid_ltx2.Rd index 3cf0642..d7953b7 100644 --- a/man/txt2vid_ltx2.Rd +++ b/man/txt2vid_ltx2.Rd @@ -157,8 +157,9 @@ Euler run at the same seed.} \item{text_encoder,tokenizer}{Gemma3 model and tokenizer (or paths; see \code{\link{load_gemma3_text_encoder}} and -\code{\link{gemma3_tokenizer}}). Ignored when \code{prompt_embeds} -is supplied.} +\code{\link{gemma3_tokenizer}}), or for LTX-2.5 the Gemma 4 model +and \code{\link{gemma4_tokenizer}} (selected by the tokenizer's +class). Ignored when \code{prompt_embeds} is supplied.} \item{width,height}{Integers. Output resolution (multiples of 32).} From 704c95c2108898bf0470c0564df5cfcfa0a7eda8 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 19:39:46 -0500 Subject: [PATCH 10/14] Keep an NF4 transformer on the GPU with no host copy ltx23_load_pipeline(gpu_components = "transformer") loads the NF4 transformer straight onto the compute device and leaves it out of the pinned host staging, so a long-lived LTX process no longer holds a second copy of it in RAM (LTX-2.5 at rest: 12.3 GB host RSS instead of about 23 GB). txt2vid_ltx2() treats such components as resident, runs the connectors where they are parked (their 2.3 GB text projection does not fit beside the 11 GB transformer on a 16 GB card), and the VAE decodes in 256 px tiles (512 px tiles OOM at 768x512x97; 256 px peak at 15.7 GB). Verified with a two-stage 768x512x97 LTX-2.5 render. --- R/txt2vid_ltx23.R | 42 +++++++++++++++++++++++++++++++------- man/ltx23_load_pipeline.Rd | 7 +++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/R/txt2vid_ltx23.R b/R/txt2vid_ltx23.R index 4a17f74..4e98c50 100644 --- a/R/txt2vid_ltx23.R +++ b/R/txt2vid_ltx23.R @@ -262,6 +262,11 @@ ltx23_unpack_audio_latents <- function(latents, num_mel_bins) { #' @param phase_offload Logical. Load the small components (connectors, #' VAEs, vocoder) to the CPU; the pipeline moves each onto the compute #' device only for its phase. +#' @param gpu_components Character. Components to load straight onto +#' \code{device} and keep there, with no host copy; currently only +#' \code{"transformer"} (NF4 artifacts). Saves the transformer's size +#' in host RAM (about 11 GB for LTX-2.5 NF4) at the cost of holding it +#' in VRAM through decode. #' @param verbose Logical. #' #' @return A list with the loaded modules and the checkpoint config, @@ -273,7 +278,9 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", transformer_device = "cpu", components = c("dit", "connectors", "vae", "audio_vae", "vocoder"), pin = TRUE, attn_chunk = NULL, - phase_offload = TRUE, verbose = TRUE) { + phase_offload = TRUE, gpu_components = character(), + verbose = TRUE) { + gpu_components <- intersect(gpu_components, "transformer") if (phase_offload) { component_device <- "cpu" } else { @@ -339,7 +346,7 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", stop("Unsupported dtype: ", dtype)) pipe <- list(config = ckpt$config, checkpoint_path = ckpt$path, - model_version = ckpt$version) + model_version = ckpt$version, gpu_components = gpu_components) load_component <- function(name, module, map_key, dev) { if (verbose) { @@ -360,7 +367,9 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", if ("dit" %in% components) { if (fp8 && identical(ckpt$format, "nf4")) { pipe$transformer <- ltx23_load_transformer_nf4( - ckpt, device = component_device, verbose = verbose + ckpt, + device = if ("transformer" %in% gpu_components) device else component_device, + verbose = verbose ) } else if (fp8) { pipe$transformer <- ltx23_load_transformer_fp8( @@ -388,6 +397,15 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", ltx23_map_vae_key, component_device ) pipe$vae$enable_tiling() + if ("transformer" %in% gpu_components) { + # Decode beside the GPU-held transformer: 512 px tiles OOM + # a 16 GB card at 768x512x97 (LTX-2.5 NF4); 256 px tiles + # peak at 15.7 GB + pipe$vae$tile_sample_min_height <- 256L + pipe$vae$tile_sample_min_width <- 256L + pipe$vae$tile_sample_stride_height <- 224L + pipe$vae$tile_sample_stride_width <- 224L + } } if ("audio_vae" %in% components) { pipe$audio_vae <- load_component( @@ -419,8 +437,10 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", message("Pinning host staging buffers...") } staging <- list() - for (nm in intersect(c("transformer", "connectors", "vae", - "audio_vae", "vocoder"), names(pipe))) { + # A component loaded straight to the GPU keeps no host copy + for (nm in setdiff(intersect(c("transformer", "connectors", "vae", + "audio_vae", "vocoder"), names(pipe)), + gpu_components)) { st <- .pin_component(pipe[[nm]]) if (!is.null(st)) { staging[[nm]] <- st @@ -724,7 +744,10 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, } else { module <- what } - if (phase_offload && !(is.character(what) && what %in% resident)) { + # Components the loader put straight on the GPU have no host + # copy to return to, so they stay put like resident ones + stay <- c(resident, pipeline$gpu_components) + if (phase_offload && !(is.character(what) && what %in% stay)) { # Decode traces capture weight tensors; drop them so the # module's GPU memory actually frees .ltx23_release_vae_traces() @@ -761,7 +784,12 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, to(device = device, dtype = compute_dtype) text_mask <- connector_embeds$attention_mask$to(device = device) } else { - onload("connectors") + # Beside a GPU-held transformer (11 GB for LTX-2.5 NF4) the + # connectors' 2.3 GB text projection does not fit a 16 GB card: + # run them where they are parked (one call per prompt) + if (!("transformer" %in% pipeline$gpu_components)) { + onload("connectors") + } connectors_device <- pipeline$connectors$video_text_proj_in$weight$device torch::with_no_grad({ conn <- pipeline$connectors( diff --git a/man/ltx23_load_pipeline.Rd b/man/ltx23_load_pipeline.Rd index c982fdb..50d25ee 100644 --- a/man/ltx23_load_pipeline.Rd +++ b/man/ltx23_load_pipeline.Rd @@ -12,6 +12,7 @@ ltx23_load_pipeline( pin = TRUE, attn_chunk = NULL, phase_offload = TRUE, + gpu_components = character(), verbose = TRUE ) } @@ -43,6 +44,12 @@ weights when loading the plain (non-fp8) checkpoint.} VAEs, vocoder) to the CPU; the pipeline moves each onto the compute device only for its phase.} +\item{gpu_components}{Character. Components to load straight onto +\code{device} and keep there, with no host copy; currently only +\code{"transformer"} (NF4 artifacts). Saves the transformer's size +in host RAM (about 11 GB for LTX-2.5 NF4) at the cost of holding it +in VRAM through decode.} + \item{verbose}{Logical.} } \value{ From 79ff228acf3039c34aee4340c971c16c3bc40d76 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 19:40:29 -0500 Subject: [PATCH 11/14] Name the model version in the LTX progress banner --- R/txt2vid_ltx23.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/txt2vid_ltx23.R b/R/txt2vid_ltx23.R index 4e98c50..0903ec0 100644 --- a/R/txt2vid_ltx23.R +++ b/R/txt2vid_ltx23.R @@ -616,7 +616,9 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, level <- .verbosity(verbose) verbose <- level == "steps" if (level != "silent") { - message(sprintf("LTX-2.3: %d frames @ %dx%d, %d steps%s", num_frames, + message(sprintf("LTX-%s: %d frames @ %dx%d, %d steps%s", + sub("^(\\d+\\.\\d+).*", "\\1", pipeline$model_version %||% "2.3"), + num_frames, width, height, length(sigmas) - 1L, if (two_stage) " (two-stage)" else "")) } From 0c6e7873b478a80044fda36e689f6ccca7f27e13 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 20:06:28 -0500 Subject: [PATCH 12/14] Revert "Keep an NF4 transformer on the GPU with no host copy" This reverts commit 704c95c2108898bf0470c0564df5cfcfa0a7eda8. --- R/txt2vid_ltx23.R | 42 +++++++------------------------------- man/ltx23_load_pipeline.Rd | 7 ------- 2 files changed, 7 insertions(+), 42 deletions(-) diff --git a/R/txt2vid_ltx23.R b/R/txt2vid_ltx23.R index 0903ec0..ca3d164 100644 --- a/R/txt2vid_ltx23.R +++ b/R/txt2vid_ltx23.R @@ -262,11 +262,6 @@ ltx23_unpack_audio_latents <- function(latents, num_mel_bins) { #' @param phase_offload Logical. Load the small components (connectors, #' VAEs, vocoder) to the CPU; the pipeline moves each onto the compute #' device only for its phase. -#' @param gpu_components Character. Components to load straight onto -#' \code{device} and keep there, with no host copy; currently only -#' \code{"transformer"} (NF4 artifacts). Saves the transformer's size -#' in host RAM (about 11 GB for LTX-2.5 NF4) at the cost of holding it -#' in VRAM through decode. #' @param verbose Logical. #' #' @return A list with the loaded modules and the checkpoint config, @@ -278,9 +273,7 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", transformer_device = "cpu", components = c("dit", "connectors", "vae", "audio_vae", "vocoder"), pin = TRUE, attn_chunk = NULL, - phase_offload = TRUE, gpu_components = character(), - verbose = TRUE) { - gpu_components <- intersect(gpu_components, "transformer") + phase_offload = TRUE, verbose = TRUE) { if (phase_offload) { component_device <- "cpu" } else { @@ -346,7 +339,7 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", stop("Unsupported dtype: ", dtype)) pipe <- list(config = ckpt$config, checkpoint_path = ckpt$path, - model_version = ckpt$version, gpu_components = gpu_components) + model_version = ckpt$version) load_component <- function(name, module, map_key, dev) { if (verbose) { @@ -367,9 +360,7 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", if ("dit" %in% components) { if (fp8 && identical(ckpt$format, "nf4")) { pipe$transformer <- ltx23_load_transformer_nf4( - ckpt, - device = if ("transformer" %in% gpu_components) device else component_device, - verbose = verbose + ckpt, device = component_device, verbose = verbose ) } else if (fp8) { pipe$transformer <- ltx23_load_transformer_fp8( @@ -397,15 +388,6 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", ltx23_map_vae_key, component_device ) pipe$vae$enable_tiling() - if ("transformer" %in% gpu_components) { - # Decode beside the GPU-held transformer: 512 px tiles OOM - # a 16 GB card at 768x512x97 (LTX-2.5 NF4); 256 px tiles - # peak at 15.7 GB - pipe$vae$tile_sample_min_height <- 256L - pipe$vae$tile_sample_min_width <- 256L - pipe$vae$tile_sample_stride_height <- 224L - pipe$vae$tile_sample_stride_width <- 224L - } } if ("audio_vae" %in% components) { pipe$audio_vae <- load_component( @@ -437,10 +419,8 @@ ltx23_load_pipeline <- function(checkpoint_path, device = "cuda", message("Pinning host staging buffers...") } staging <- list() - # A component loaded straight to the GPU keeps no host copy - for (nm in setdiff(intersect(c("transformer", "connectors", "vae", - "audio_vae", "vocoder"), names(pipe)), - gpu_components)) { + for (nm in intersect(c("transformer", "connectors", "vae", + "audio_vae", "vocoder"), names(pipe))) { st <- .pin_component(pipe[[nm]]) if (!is.null(st)) { staging[[nm]] <- st @@ -746,10 +726,7 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, } else { module <- what } - # Components the loader put straight on the GPU have no host - # copy to return to, so they stay put like resident ones - stay <- c(resident, pipeline$gpu_components) - if (phase_offload && !(is.character(what) && what %in% stay)) { + if (phase_offload && !(is.character(what) && what %in% resident)) { # Decode traces capture weight tensors; drop them so the # module's GPU memory actually frees .ltx23_release_vae_traces() @@ -786,12 +763,7 @@ txt2vid_ltx2 <- function(prompt, pipeline, text_encoder = NULL, to(device = device, dtype = compute_dtype) text_mask <- connector_embeds$attention_mask$to(device = device) } else { - # Beside a GPU-held transformer (11 GB for LTX-2.5 NF4) the - # connectors' 2.3 GB text projection does not fit a 16 GB card: - # run them where they are parked (one call per prompt) - if (!("transformer" %in% pipeline$gpu_components)) { - onload("connectors") - } + onload("connectors") connectors_device <- pipeline$connectors$video_text_proj_in$weight$device torch::with_no_grad({ conn <- pipeline$connectors( diff --git a/man/ltx23_load_pipeline.Rd b/man/ltx23_load_pipeline.Rd index 50d25ee..c982fdb 100644 --- a/man/ltx23_load_pipeline.Rd +++ b/man/ltx23_load_pipeline.Rd @@ -12,7 +12,6 @@ ltx23_load_pipeline( pin = TRUE, attn_chunk = NULL, phase_offload = TRUE, - gpu_components = character(), verbose = TRUE ) } @@ -44,12 +43,6 @@ weights when loading the plain (non-fp8) checkpoint.} VAEs, vocoder) to the CPU; the pipeline moves each onto the compute device only for its phase.} -\item{gpu_components}{Character. Components to load straight onto -\code{device} and keep there, with no host copy; currently only -\code{"transformer"} (NF4 artifacts). Saves the transformer's size -in host RAM (about 11 GB for LTX-2.5 NF4) at the cost of holding it -in VRAM through decode.} - \item{verbose}{Logical.} } \value{ From 75c3065832e98dd3958d4edd4927ac265465f2f2 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 20:17:54 -0500 Subject: [PATCH 13/14] Serve LTX-2.5 through resident_load like LTX-2.3 gpu.ctl loads LTX as resident_load("ltx", checkpoint_path = , text_encoder = , tokenizer = ...). For 2.5: - ltx23_quantize_nf4() accepts an open checkpoint, so ltx25_open_checkpoint() over the split files writes ONE self-contained artifact (transformer, connectors and text projection, conv VAE, audio VAE, vocoder), the shape of the 2.3 artifact. A transformer that is already NF4 is bundled as is, so the artifact can be built without the 42 GB bf16 source. Shards are named after the model version. - resident_load() loads a Gemma 4 artifact with load_gemma4_text_encoder() and gemma4_tokenizer() (the artifact carries its tokenizer), and the Gemma 3 path otherwise. Real bundle: 7196 tensors (1344 NF4), 10 shards, 19.52 GB, 7 min, peak RSS 6.1 GB. --- R/nf4_ltx23.R | 31 ++++++++++++--- R/quantize_gemma4.R | 20 ++++++++++ R/resident.R | 21 +++++++--- inst/tinytest/test_checkpoint_ltx25.R | 57 +++++++++++++++++++++++++++ man/ltx23_quantize_nf4.Rd | 7 +++- man/resident_load.Rd | 4 +- 6 files changed, 128 insertions(+), 12 deletions(-) diff --git a/R/nf4_ltx23.R b/R/nf4_ltx23.R index 973e315..0ba3a36 100644 --- a/R/nf4_ltx23.R +++ b/R/nf4_ltx23.R @@ -255,7 +255,12 @@ ltx23_nf4_linear <- torch::nn_module( #' Non-cast tensors are copied through unchanged. The manifest carries #' \code{format = "nf4"}. #' -#' @param checkpoint_path Source .safetensors (bf16 single file). +#' @param checkpoint_path Source .safetensors (bf16 single file), or an +#' open \code{ltx23_checkpoint}. For LTX-2.5, pass +#' \code{\link{ltx25_open_checkpoint}} to write one self-contained +#' artifact (transformer, connectors and text projection, VAEs, +#' vocoder); when its transformer is already an NF4 artifact, the +#' NF4 tensors are bundled as they are, with no re-quantization. #' @param output_dir Output directory for shards + manifest; NULL (the #' default) resolves under tools::R_user_dir("diffuseR", "data"). #' @param shard_bytes Numeric. Target shard size in bytes. The default @@ -289,7 +294,19 @@ ltx23_quantize_nf4 <- function(checkpoint_path, output_dir = NULL, } dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) - ckpt <- ltx23_open_checkpoint(checkpoint_path) + if (inherits(checkpoint_path, "ltx23_checkpoint")) { + # An open checkpoint, e.g. the LTX-2.5 file set from + # ltx25_open_checkpoint(); one already in NF4 is bundled as is + ckpt <- checkpoint_path + source <- basename(unname(ckpt$sources %||% ckpt$path)) + source <- source[!is.na(source)] + } else { + ckpt <- ltx23_open_checkpoint(checkpoint_path) + source <- basename(checkpoint_path) + } + passthrough <- identical(ckpt$format, "nf4") + prefix <- sprintf("ltx%s-nf4", + sub("^(\\d+\\.\\d+).*", "\\1", ckpt$version %||% "2.3")) shard <- list() shard_size <- 0 @@ -300,7 +317,7 @@ ltx23_quantize_nf4 <- function(checkpoint_path, output_dir = NULL, if (!length(shard)) { return() } - fname <- sprintf("ltx2.3-nf4-%05d.safetensors", + fname <- sprintf("%s-%05d.safetensors", prefix, length(shard_files) + 1L) safetensors::safe_save_file(shard, file.path(output_dir, fname)) shard_files[[length(shard_files) + 1L]] <<- fname @@ -319,7 +336,11 @@ ltx23_quantize_nf4 <- function(checkpoint_path, output_dir = NULL, tensor <- ckpt$handle$get_tensor(key) mapped <- ltx23_map_dit_key(key) - if (startsWith(key, "model.diffusion_model.") && + if (passthrough) { + shard[[key]] <- tensor + shard_size <- shard_size + prod(tensor$shape) * tensor$element_size() + n_cast <- n_cast + endsWith(key, "_absmax") + } else if (startsWith(key, "model.diffusion_model.") && ltx23_is_fp8_cast_key(mapped)) { torch::with_no_grad({ q <- ltx23_nf4_quantize(tensor) @@ -349,7 +370,7 @@ ltx23_quantize_nf4 <- function(checkpoint_path, output_dir = NULL, flush_shard() manifest <- list( - source = basename(checkpoint_path), + source = source, model_version = ckpt$version, format = "nf4", shards = shard_files, diff --git a/R/quantize_gemma4.R b/R/quantize_gemma4.R index 4d5851d..af604c6 100644 --- a/R/quantize_gemma4.R +++ b/R/quantize_gemma4.R @@ -236,3 +236,23 @@ load_gemma4_nf4 <- function(artifact_dir, device = "cuda", dtype = "bfloat16", } model } + +# TRUE when `path` is a Gemma 4 encoder: its NF4 artifact directory, or the +# LTX-2.5 single file (gemma_config metadata). Lets a caller holding a path +# pick the Gemma 4 loader over the Gemma 3 one. +.is_gemma4_artifact <- function(path) { + if (!is.character(path) || length(path) != 1L) { + return(FALSE) + } + manifest <- file.path(path, "manifest.json") + if (file.exists(manifest)) { + return(identical(jsonlite::fromJSON(manifest)$model, "gemma4")) + } + if (!file.exists(path) || dir.exists(path)) { + return(FALSE) + } + meta <- tryCatch( + ltx23_open_checkpoint(path, require_version = NULL)$handle$metadata[["__metadata__"]], + error = function(e) NULL) + !is.null(meta[["gemma_config"]]) +} diff --git a/R/resident.R b/R/resident.R index 0ace6d7..d46f6be 100644 --- a/R/resident.R +++ b/R/resident.R @@ -236,7 +236,9 @@ #' \code{\link{download_sdxl}} cache). #' @param verbose Print progress messages. #' @param text_encoder,tokenizer \code{ltx} only: paths to the Gemma3 encoder -#' artifact and the tokenizer directory. \code{\link{ltx23_load_pipeline}} +#' artifact and the tokenizer directory, or for LTX-2.5 the Gemma 4 NF4 +#' artifact (\code{\link{gemma4_quantize_nf4}}) for both, since it carries +#' its tokenizer. \code{\link{ltx23_load_pipeline}} #' does not load these -- \code{\link{txt2vid_ltx2}} takes them per call -- #' so a handle built without them can be activated and cannot generate. #' Given here they are loaded ONCE, pinned on the host, and passed to every @@ -346,10 +348,19 @@ resident_load <- function(model = c("flux2", "flux1", "zimage", "ltx", stop("text_encoder needs a tokenizer: the encode takes both", call. = FALSE) } - if (verbose) message("Loading the Gemma3 text encoder (pinned)...") - res$text_encoder <- load_gemma3_text_encoder( - text_encoder, device = "cpu", pin = TRUE, verbose = verbose) - res$tokenizer <- gemma3_tokenizer(tokenizer) + # LTX-2.5 encodes with Gemma 4; its NF4 artifact carries the + # tokenizer too. txt2vid_ltx2() picks the encode by tokenizer class. + if (.is_gemma4_artifact(text_encoder)) { + if (verbose) message("Loading the Gemma 4 text encoder (pinned)...") + res$text_encoder <- load_gemma4_text_encoder( + text_encoder, device = "cpu", pin = TRUE, verbose = verbose) + res$tokenizer <- gemma4_tokenizer(tokenizer) + } else { + if (verbose) message("Loading the Gemma3 text encoder (pinned)...") + res$text_encoder <- load_gemma3_text_encoder( + text_encoder, device = "cpu", pin = TRUE, verbose = verbose) + res$tokenizer <- gemma3_tokenizer(tokenizer) + } ## Counted, so `resident_status()` reports what the process actually ## holds. A pinned set omitted from the total reads as headroom that ## is not there, and the fleet's admission arithmetic is downstream diff --git a/inst/tinytest/test_checkpoint_ltx25.R b/inst/tinytest/test_checkpoint_ltx25.R index fdb3f8d..d72c32f 100644 --- a/inst/tinytest/test_checkpoint_ltx25.R +++ b/inst/tinytest/test_checkpoint_ltx25.R @@ -90,6 +90,63 @@ local({ path("vaediff.safetensors")), "convolutional") expect_error(ltx25_open_checkpoint(path("dit.safetensors"), path("vae.safetensors"), path("vae.safetensors")), "text_embedding_projection") + + # --- One self-contained NF4 artifact from the split files ----------------- + q_key <- "model.diffusion_model.transformer_blocks.0.attn1.to_q.weight" + torch::torch_manual_seed(3) + dq <- list(t(c(1, 2)), t(3), torch::torch_randn(8L, 16L)) + names(dq) <- c("model.diffusion_model.patchify_proj.weight", + "model.diffusion_model.video_embeddings_connector.learnable_registers", + q_key) + save(dq, "ditq.safetensors", + config = list(transformer = list(ff_bias = FALSE))) + art <- path("bundle") + m <- ltx23_quantize_nf4( + ltx25_open_checkpoint(path("ditq.safetensors"), path("te.safetensors"), + path("vae.safetensors"), path("audio.safetensors")), + art, verbose = FALSE) + expect_equal(m$model_version, "2.5.0") + expect_equal(m$nf4_cast, 1L) + expect_true(all(startsWith(m$shards, "ltx2.5-nf4-"))) + expect_equal(length(m$source), 4L) + ab <- ltx23_open_fp8_checkpoint(art) + expect_equal(ab$format, "nf4") + expect_true(all(c(paste0(q_key, "_absmax"), + "vae.per_channel_statistics.mean-of-means", + "text_embedding_projection.video_aggregate_embed.weight", + "vocoder.vocoder.conv_in.weight") %in% ab$keys)) + expect_false(any(grepl("^model\\.layers", ab$keys))) + expect_equal(ab$config$vae$latent_channels, 128L) + expect_false(ab$config$transformer$ff_bias) + expect_equal(as.numeric(ab$handle$get_tensor("vocoder.vocoder.conv_in.weight")), 10) + + # A transformer that is already NF4 is bundled as is, not re-quantized + dit_art <- path("dit-nf4") + ltx23_quantize_nf4(path("ditq.safetensors"), dit_art, verbose = FALSE) + art2 <- path("bundle2") + m2 <- ltx23_quantize_nf4( + ltx25_open_checkpoint(dit_art, path("te.safetensors"), path("vae.safetensors"), + path("audio.safetensors")), + art2, verbose = FALSE) + expect_equal(m2$nf4_cast, 1L) + b2 <- ltx23_open_fp8_checkpoint(art2) + expect_equal(sort(b2$keys), sort(ab$keys)) + expect_true(torch::torch_equal(b2$handle$get_tensor(q_key), + ab$handle$get_tensor(q_key))) + expect_true(torch::torch_equal(b2$handle$get_tensor(paste0(q_key, "_absmax")), + ab$handle$get_tensor(paste0(q_key, "_absmax")))) + + # The Gemma 4 artifact is told apart from a Gemma 3 one by its manifest + g4 <- path("g4"); dir.create(g4) + jsonlite::write_json(list(model = "gemma4"), file.path(g4, "manifest.json"), + auto_unbox = TRUE) + g3 <- path("g3"); dir.create(g3) + jsonlite::write_json(list(format = "nf4"), file.path(g3, "manifest.json"), + auto_unbox = TRUE) + expect_true(diffuseR:::.is_gemma4_artifact(g4)) + expect_false(diffuseR:::.is_gemma4_artifact(g3)) + expect_false(diffuseR:::.is_gemma4_artifact(path("te.safetensors"))) + expect_false(diffuseR:::.is_gemma4_artifact(NULL)) }) # An encoder already on the CPU encodes there; staging or the GPU decides otherwise diff --git a/man/ltx23_quantize_nf4.Rd b/man/ltx23_quantize_nf4.Rd index 9b1566d..a6a4edf 100644 --- a/man/ltx23_quantize_nf4.Rd +++ b/man/ltx23_quantize_nf4.Rd @@ -12,7 +12,12 @@ ltx23_quantize_nf4( ) } \arguments{ -\item{checkpoint_path}{Source .safetensors (bf16 single file).} +\item{checkpoint_path}{Source .safetensors (bf16 single file), or an +open \code{ltx23_checkpoint}. For LTX-2.5, pass +\code{\link{ltx25_open_checkpoint}} to write one self-contained +artifact (transformer, connectors and text projection, VAEs, +vocoder); when its transformer is already an NF4 artifact, the +NF4 tensors are bundled as they are, with no re-quantization.} \item{output_dir}{Output directory for shards + manifest; NULL (the default) resolves under tools::R_user_dir("diffuseR", "data").} diff --git a/man/resident_load.Rd b/man/resident_load.Rd index 808e95c..ed2313f 100644 --- a/man/resident_load.Rd +++ b/man/resident_load.Rd @@ -27,7 +27,9 @@ resident_load( \item{verbose}{Print progress messages.} \item{text_encoder,tokenizer}{\code{ltx} only: paths to the Gemma3 encoder -artifact and the tokenizer directory. \code{\link{ltx23_load_pipeline}} +artifact and the tokenizer directory, or for LTX-2.5 the Gemma 4 NF4 +artifact (\code{\link{gemma4_quantize_nf4}}) for both, since it carries +its tokenizer. \code{\link{ltx23_load_pipeline}} does not load these -- \code{\link{txt2vid_ltx2}} takes them per call -- so a handle built without them can be activated and cannot generate. Given here they are loaded ONCE, pinned on the host, and passed to every From 43c0b33dd2c9eb61817cdf6781b0273871b65fea Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Thu, 22 Oct 2026 20:18:06 -0500 Subject: [PATCH 14/14] Bump version to 0.2.2.14 --- DESCRIPTION | 2 +- NEWS.md | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 923d34c..aa5c3d3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: diffuseR Title: Functional Interface to Diffusion Models in R -Version: 0.2.2.13 +Version: 0.2.2.14 Authors@R: c( person("Troy", "Hernandez", email = "troy@cornball.ai", role = c("aut", "cre"), comment = c(ORCID = "0009-0005-4248-604X")), diff --git a/NEWS.md b/NEWS.md index a9d6d3f..f1bca17 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,28 @@ +# diffuseR 0.2.2.14 + +* **LTX-2.5 (distilled) text-to-video.** The 2.3 pipeline runs the 2.5 + checkpoints: + - Transformer flags come from the embedded config (`ff_bias = FALSE`, + keyframe position embedding); `ltx23_open_checkpoint()` accepts + model version 2.5. + - `txt2vid_ltx2(sampler = "auto")` uses the ancestral Euler sampler + for 2.5 (stage noise from `seed + 10000` / `seed + 20000`), Euler + for 2.3. + - New Gemma 4 text encoder: `gemma4_text_model()`, + `load_gemma4_text_encoder()`, `encode_with_gemma4()`, an exact + ranked-BPE `gemma4_tokenizer()`, and `gemma4_quantize_nf4()` / + `load_gemma4_nf4()`. `txt2vid_ltx2()` picks Gemma 4 by tokenizer + class. + - `ltx25_open_checkpoint()` presents the split 2.5 files (transformer, + encoder file with the text projection, conv VAE, audio VAE + + vocoder) as one checkpoint; `ltx23_load_pipeline()` takes it, and + `ltx23_quantize_nf4()` writes it as one self-contained NF4 artifact. + - `resident_load("ltx")` loads a Gemma 4 encoder artifact. + - The diffusion-decoder VAE, duration head and temporal upsampler are + not supported yet. +* The LTX fp8/NF4 and Gemma 4 quantizers collect garbage per tensor, so + quantizing no longer outgrows host RAM. + # diffuseR 0.2.2.13 * **`zimage_load_pipeline()` takes `revision`**, as