code wiki / (root) / nx_genlora.nx

nx_genlora.nx

buildroot/runtime/nx_genlora.nx

10609 B242 linesdepth 7pulls 18 transitivereach 2 importersview sourcekind library
docsdependenciesstructsconstsfunctions

about

nx_genlora.nx -- apply LoRA adapters to a base weight. Sovereign, model-agnostic LoRA support. W' = W + (alpha / rank) * multiplier * (up @ down) read out of sd.cpp's lora.hpp, not guessed: `scale_value = alpha / rank; scale_value *= multiplier`. With no `.alpha` tensor, alpha defaults to rank, so the scale is the multiplier alone. ⚠TWO THINGS VARY INDEPENDENTLY, AND BOTH MUST BE HANDLED: 1. THE NAMING CONVENTION. base model.diffusion_model.layers.0.attention.qkv.weight kohya lora_unet_layers_0_attention_qkv.lora_down.weight (flattened, underscores) peft diffusion_model.layers.0.attention.to_q.lora_A.weight (dotted, A=down B=up) 2. WHETHER THE ADAPTER TRAINED THE **FUSED** MODULE OR THE UNFUSED ONES. This is the part that bites. The Z-Image checkpoint stores ONE fused `attention.qkv.weight` [11520, 3840]. The kohya adapter here matches it directly. But all NINE peft adapters in the same folder train `to_q` / `to_k` / `to_v` SEPARATELY -- there is no `attention.qkv` key in any of them. A name-only lookup therefore finds nothing, folds nothing, and returns "this adapter does not cover this layer" for EVERY layer of EVERY peft adapter, with no error anywhere. ★★★★★ AN ADAPTER THAT MATCHES NO KEYS IS INDISTINGUISHABLE FROM ONE THAT IS WORKING WEAKLY. ⇒ a fused base weight must be folded SLICE-WISE from the unfused adapter modules: to_q -> rows [0, out/3) to_k -> [out/3, 2out/3) to_v -> [2out/3, out) ★ MEASURED CAVEAT worth more than the code: on Z-Image layer 0 the kohya adapter moves the activation by only 0.86% while the oracle's own Q8_0 error is 0.6% median. Fitting the scale by least squares against the oracle recovered 0.1725 where alpha/rank is 0.1667 -- consistent, but INSIDE THE NOISE. ★★★★★ YOU CANNOT VALIDATE AN ADAPTER ON A LAYER WHERE THE ADAPTER BARELY ACTS; grade against an EXACT reference (nx_gen_lora_verify does). WHY FOLD RATHER THAN APPLY AT RUNTIME: sd.cpp keeps the adapter separate and pays 2*rank*(in+out)*tokens every step. Folding pays 2*rank*in*out ONCE and leaves the hot matmul byte-identical to the un-adapted path -- so an adapter costs nothing per step and cannot slow down or perturb the kernel. license_tier: ORIGINAL

dependencies 8 imports · 2 importers

nx_syscalls.nx nx_le.nx nx_f32.nx nx_f32_div.nx nx_f32_cvt.nx nx_f16.nx nx_strconv.nx nx_genweights.nx nx_genlora.nx nx_gen_lora_repr.nx nx_gen_lora_verify.nx

imports: nx_syscalls.nxnx_le.nxnx_f32.nxnx_f32_div.nxnx_f32_cvt.nxnx_f16.nxnx_strconv.nxnx_genweights.nx

imported by: nx_gen_lora_repr.nxnx_gen_lora_verify.nx

structs

none

consts

45const LORA_MAGIC_1024: i64 = 1024
49const LORA_ABSENT: i64 = 0
50const LORA_APPLIED: i64 = 1
51const LORA_BAD_SHAPE: i64 = 0 - 10
52const LORA_BAD_PAIR: i64 = 0 - 11
53const LORA_BAD_TYPE: i64 = 0 - 12

functions

55func _lo_len(s: *u8) -> i64
60func _lo_app(dst: *u8, at: i64, src: *u8) -> i64
67func _lo_ends(s: *u8, suf: *u8) -> i64
called by 1: nx_lora_fold calls 1: _lo_len
77func _lo_retail(stem: *u8, nold: i64, rep: *u8) -> i64
called by 1: nx_lora_fold calls 2: _lo_app_lo_len
82func nx_lora_key_kohya(out: *u8, base: *u8) -> i64
called by 1: nx_lora_fold calls 2: _lo_len_lo_app
104func nx_lora_key_peft(out: *u8, base: *u8) -> i64
called by 1: nx_lora_fold calls 1: _lo_len
119func _lo_probe(lw: *i64, stem: *u8, suffix: *u8, nm: *u8, inf: *i64) -> i64
129func nx_lora_fold_slice(lw: *i64, stem: *u8, dn_suf: *u8, up_suf: *u8, W: *u8,
196func nx_lora_fold(lw: *i64, base: *u8, W: *u8, in_dim: i64, out_dim: i64, mult_f32: i64) -> i64