Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d957225
granite-switch: add llama.cpp backend (POC, CPU)
barvhaim Jun 18, 2026
6e734e8
granite-switch: add Mac (Metal) build + mid-sequence switch demo script
barvhaim Jun 18, 2026
af48dfa
granite-switch mac demo: add -no-cnv so each run is one-shot
barvhaim Jun 18, 2026
16c9ad5
granite-switch: replace global sticky index with in-graph router atte…
barvhaim Jun 22, 2026
30ffe08
granite-switch: drop scratch tests and mac demo for upstream PR
barvhaim Jun 24, 2026
0ac7c96
granite-switch: trim comments to match native llama.cpp style
barvhaim Jun 24, 2026
b17c0bc
granite-switch: trim conversion comments to match native style
barvhaim Jun 24, 2026
7626399
granite-switch: drop unused adapter_ranks metadata
barvhaim Jun 25, 2026
e193ea5
granite-switch: rename arch to graniteswitch and drop obid alias
barvhaim Jun 25, 2026
2b179f0
granite-switch: fix non-ASCII comments and document router gain assum…
barvhaim Jun 25, 2026
3041ba8
granite-switch: drop section comments from constants.py to match nati…
barvhaim Jun 28, 2026
8c35307
granite-switch: add functional tensor block comments matching Granite…
barvhaim Jun 28, 2026
36936ad
granite-switch: clarify n_expert_used comment
barvhaim Jun 28, 2026
dba81e3
granite-switch: note n_layer_nextn reuse has no MTP
barvhaim Jun 28, 2026
26f2843
granite-switch: rename source file and apply review nits
barvhaim Jun 30, 2026
116c611
granite-switch: don't force LoRA tensors to F16, follow --outtype ins…
barvhaim Jun 30, 2026
6be5d0c
granite-switch: drop redundant _permute_qk wrapper, call LlamaModel.p…
barvhaim Jun 30, 2026
059401c
granite-switch: read router gain from GGUF (control_token_gain) inste…
barvhaim Jun 30, 2026
8d71a5c
granite-switch: derive n_slots()
barvhaim Jun 30, 2026
afa1b7e
granite-switch: move llm_graph_input_switch into granite-switch.cpp
barvhaim Jun 30, 2026
5b2cb93
granite-switch: cut AI-style narration comments
barvhaim Jun 30, 2026
d385fc2
granite-switch: collapse multi-line comments
barvhaim Jun 30, 2026
768e608
granite-switch: rename control_token_* maps to adapter_token_*
barvhaim Jun 30, 2026
03e46c1
granite-switch: cut noise comments
barvhaim Jun 30, 2026
24e06e9
granite-switch: rename embedded LoRA tensors to <base>.lora_a/lora_b
barvhaim Jun 30, 2026
613bc19
granite-switch: GGML_ASSERT token input to avoid UB on embeddings
barvhaim Jul 1, 2026
472d1c9
granite-switch: TODO for raw embedding input support
barvhaim Jul 1, 2026
c601484
granite-switch: collapse LoRA tensor constants to .lora_a/.lora_b suffix
barvhaim Jul 1, 2026
622eb21
granite-switch: drop n_expert_used hack, guard mul_mat_id buft probe
barvhaim Jul 2, 2026
ad04ce8
granite-switch: stop forcing dense expert counts, read from config
barvhaim Jul 2, 2026
1d15ea3
granite-switch: renamed control_token_gain metadata key to router_gain
barvhaim Jul 2, 2026
e99c056
granite-switch: trim header comments to match native style
barvhaim Jul 2, 2026
46d882e
granite-switch: collapse LoRA tensors to base name + suffix
barvhaim Jul 2, 2026
d99296f
granite-switch: inline suffix checks in tensor op resolution
barvhaim Jul 7, 2026
c57b00f
granite-switch: drop switch-lora struct comment
barvhaim Jul 7, 2026
d2ca90b
granite-switch: guard router layer index and inline n_slots
barvhaim Jul 8, 2026
11890d3
granite-switch: group adapter metadata under {arch}.adapters.* namespace
barvhaim Jul 8, 2026
ae2ae69
granite-switch: add hparams.has_rope(il) for KV-shift rope skipping
barvhaim Jul 8, 2026
d886073
granite-switch: skip arch in test-llama-archs (adapter fixture missin…
barvhaim Jul 12, 2026
2204806
granite-switch: Keys.Adapters namespace + simplify n_slots
barvhaim Jul 12, 2026
5669857
granite-switch: validate substitute token ids against n_vocab
barvhaim Jul 26, 2026
a7271ce
granite-switch: bound adapter count and lora rank from GGUF
barvhaim Jul 26, 2026
64278bb
granite-switch: reject MTP context type when router_layer is set
barvhaim Jul 26, 2026
34f5607
granite-switch: throw on bad adapter metadata instead of GGML_ASSERT
barvhaim Jul 26, 2026
7cc8ba1
granite-switch: use ASCII +/- in router K signal comment
barvhaim Jul 26, 2026
118cf8a
granite-switch: document n_layer_nextn repurpose and its leak points
barvhaim Jul 26, 2026
65cb201
granite-switch: gate lora_a/lora_b op mapping on router_layer
barvhaim Jul 26, 2026
15e473d
granite-switch: label all three preview model sizes
barvhaim Jul 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions conversion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"GraniteMoeForCausalLM": "granite",
"GraniteMoeHybridForCausalLM": "granite",
"GraniteMoeSharedForCausalLM": "granite",
"GraniteSwitchForCausalLM": "granite",
"GraniteSpeechForConditionalGeneration": "granite",
"GraniteSpeechPlusForConditionalGeneration": "granite",
"Grok1ForCausalLM": "grok",
Expand Down
160 changes: 160 additions & 0 deletions conversion/granite.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,166 @@ def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iter
yield from super().modify_tensors(data_torch, name, bid)


@ModelBase.register("GraniteSwitchForCausalLM")
class GraniteSwitchModel(GraniteMoeModel):
"""Dense, all-attention Granite with N per-token embedded LoRA adapters, stacked
over the adapter dim with a zero adapter at slot 0 (N = num_adapters + 1)."""
model_arch = gguf.MODEL_ARCH.GRANITE_SWITCH

# permute q/k per-slice below (NORM-rope layout), not via the parent's auto-permute
undo_permute = False

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# the weightless switch reserves one cache slot: one fewer block than num_hidden_layers
self.block_count = self.block_count - 1
self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count)

self._n_adapters = int(self.hparams["num_adapters"])
self._max_lora_rank = int(self.hparams["max_lora_rank"])
self._n_slots = self._n_adapters + 1 # +1 for the zero slot at index 0

n_head = int(self.hparams["num_attention_heads"])
n_kv_head = int(self.hparams["num_key_value_heads"])
head_dim = (
self.hparams.get("projection_head_dim")
or self.hparams.get("head_dim")
or (self.hparams["hidden_size"] // n_head)
)
self._n_head = n_head
self._n_kv_head = n_kv_head
self._head_dim = int(head_dim)
self._q_size = n_head * self._head_dim
self._kv_size = n_kv_head * self._head_dim

def set_gguf_parameters(self):
super().set_gguf_parameters()

# dense: pin expert_used_count to 0 (config carries a leftover num_experts_per_tok)
if not self.hparams.get("num_local_experts"):
self.gguf_writer.add_expert_used_count(0)

self.gguf_writer.add_adapter_count(self._n_adapters)
self.gguf_writer.add_adapter_lora_rank(self._max_lora_rank)
self.gguf_writer.add_adapter_token_ids_activate(self.hparams["adapter_token_ids"])
self.gguf_writer.add_adapter_token_ids_substitute(self.hparams["adapter_substitute_token_ids"])
router_gain = float(self.hparams.get("control_token_gain", 15.0))
self.gguf_writer.add_adapter_router_gain(router_gain)
logger.info("gguf: (graniteswitch) num_adapters=%s max_lora_rank=%s n_slots=%s router_gain=%s", self._n_adapters, self._max_lora_rank, self._n_slots, router_gain)

def _lora_a(self, data: Tensor) -> Tensor:
# on-disk A: [n_adapters, 1, max_rank, in] -> [n_adapters+1, max_rank, in]
a = data.squeeze(1)
zero = torch.zeros_like(a[:1])
return torch.cat([zero, a], dim=0).contiguous()

def _lora_b(self, data: Tensor, permute_n_head: int | None = None) -> Tensor:
# on-disk B: [n_adapters, 1, out, max_rank] -> [n_adapters+1, out, max_rank]
b = data.squeeze(1)
if permute_n_head is not None:
# permute each adapter's B output rows to match the permuted q/k base
b = torch.stack([self.permute(b[i], permute_n_head, permute_n_head) for i in range(b.shape[0])], dim=0)
zero = torch.zeros_like(b[:1])
return torch.cat([zero, b], dim=0).contiguous()

def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
T = gguf.MODEL_TENSOR

# skip the weightless switch + control-token buffers (rebuilt at load time)
bare = name.split(".")[-1]
if (
name.startswith("model.switch.") or name.startswith("switch.")
or bare in ("adapter_token_ids", "control_to_substitute_lut")
):
return

if "self_attn.qkv_proj" in name:
if name.endswith("base_layer.weight"):
# fused [q|k|v] rows: permute q/k row-blocks for ggml's NORM-rope layout
q, k, v = data_torch.split([self._q_size, self._kv_size, self._kv_size], dim=0)
q = self.permute(q, self._n_head, self._n_head)
k = self.permute(k, self._n_kv_head, self._n_kv_head)
fused = torch.cat([q, k, v], dim=0)
yield (self.format_tensor_name(T.ATTN_QKV, bid), fused)
return
if "lora_A_slices." in name:
slot = int(name.rsplit(".", 1)[1])
key = {0: T.ATTN_Q, 1: T.ATTN_K, 2: T.ATTN_V}[slot]
yield (self.format_tensor_name(key, bid, suffix=".lora_a"), self._lora_a(data_torch))
return
if "lora_B_slices." in name:
slot = int(name.rsplit(".", 1)[1])
key, ph = {
0: (T.ATTN_Q, self._n_head),
1: (T.ATTN_K, self._n_kv_head),
2: (T.ATTN_V, None),
}[slot]
yield (self.format_tensor_name(key, bid, suffix=".lora_b"), self._lora_b(data_torch, ph))
return
raise ValueError(f"Unexpected qkv_proj tensor: {name}")

if "self_attn.o_proj" in name:
if name.endswith("base_layer.weight"):
yield (self.format_tensor_name(T.ATTN_OUT, bid), data_torch)
return
if name.endswith("lora_A"):
yield (self.format_tensor_name(T.ATTN_OUT, bid, suffix=".lora_a"), self._lora_a(data_torch))
return
if name.endswith("lora_B"):
yield (self.format_tensor_name(T.ATTN_OUT, bid, suffix=".lora_b"), self._lora_b(data_torch))
return
raise ValueError(f"Unexpected o_proj tensor: {name}")

if "shared_mlp.input_linear" in name:
ffn = self.hparams["shared_intermediate_size"]
if name.endswith("base_layer.weight"):
gate, up = data_torch.split([ffn, ffn], dim=0)
yield (self.format_tensor_name(T.FFN_GATE, bid), gate)
yield (self.format_tensor_name(T.FFN_UP, bid), up)
return
if "lora_A_slices." in name:
slot = int(name.rsplit(".", 1)[1])
key = {0: T.FFN_GATE, 1: T.FFN_UP}[slot]
yield (self.format_tensor_name(key, bid, suffix=".lora_a"), self._lora_a(data_torch))
return
if "lora_B_slices." in name:
slot = int(name.rsplit(".", 1)[1])
key = {0: T.FFN_GATE, 1: T.FFN_UP}[slot]
yield (self.format_tensor_name(key, bid, suffix=".lora_b"), self._lora_b(data_torch))
return
raise ValueError(f"Unexpected shared_mlp.input_linear tensor: {name}")

if "shared_mlp.output_linear" in name:
if name.endswith("base_layer.weight"):
yield (self.format_tensor_name(T.FFN_DOWN, bid), data_torch)
return
if name.endswith("lora_A"):
yield (self.format_tensor_name(T.FFN_DOWN, bid, suffix=".lora_a"), self._lora_a(data_torch))
return
if name.endswith("lora_B"):
yield (self.format_tensor_name(T.FFN_DOWN, bid, suffix=".lora_b"), self._lora_b(data_torch))
return
raise ValueError(f"Unexpected shared_mlp.output_linear tensor: {name}")

if bid is not None and ".layers." in name and (
"input_layernorm" in name or "post_attention_layernorm" in name
):
key = T.ATTN_NORM if "input_layernorm" in name else T.FFN_NORM
yield (self.format_tensor_name(key, bid), data_torch)
return

if name in ("model.embed_tokens.weight", "embed_tokens.weight"):
yield (self.format_tensor_name(T.TOKEN_EMBD), data_torch)
return
if name in ("model.norm.weight", "norm.weight"):
yield (self.format_tensor_name(T.OUTPUT_NORM), data_torch)
return
if name == "lm_head.weight":
return # tied to token_embd

raise ValueError(f"graniteswitch: unhandled tensor {name!r} (bid={bid})")


@ModelBase.register("GraniteMoeHybridForCausalLM", "BambaForCausalLM")
class GraniteHybridModel(Mamba2Model, GraniteMoeModel):
"""GraniteHybrid is a hybrid SSM + Attention model that uses Mamba2 SSM
Expand Down
24 changes: 24 additions & 0 deletions gguf-py/gguf/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ class LLM:
BLOCK_SIZE = "{arch}.block_size"
NORM_BEFORE_RESIDUAL = "{arch}.norm_before_residual"

class Adapters:
COUNT = "{arch}.adapters.count"
TOKEN_IDS_ACTIVATE = "{arch}.adapters.token_ids_activate"
TOKEN_IDS_SUBSTITUTE = "{arch}.adapters.token_ids_substitute"
LORA_RANK = "{arch}.adapters.lora_rank"
ROUTER_GAIN = "{arch}.adapters.router_gain"

class Attention:
HEAD_COUNT = "{arch}.attention.head_count"
HEAD_COUNT_KV = "{arch}.attention.head_count_kv"
Expand Down Expand Up @@ -500,6 +507,7 @@ class MODEL_ARCH(IntEnum):
GRANITE = auto()
GRANITE_MOE = auto()
GRANITE_HYBRID = auto()
GRANITE_SWITCH = auto()
CHAMELEON = auto()
WAVTOKENIZER_DEC = auto()
PLM = auto()
Expand Down Expand Up @@ -1082,6 +1090,7 @@ class MODEL_TENSOR(IntEnum):
MODEL_ARCH.GRANITE: "granite",
MODEL_ARCH.GRANITE_MOE: "granitemoe",
MODEL_ARCH.GRANITE_HYBRID: "granitehybrid",
MODEL_ARCH.GRANITE_SWITCH: "graniteswitch",
MODEL_ARCH.CHAMELEON: "chameleon",
MODEL_ARCH.WAVTOKENIZER_DEC: "wavtokenizer-dec",
MODEL_ARCH.PLM: "plm",
Expand Down Expand Up @@ -3674,6 +3683,21 @@ class MODEL_TENSOR(IntEnum):
MODEL_TENSOR.FFN_DOWN,
MODEL_TENSOR.FFN_UP,
],
MODEL_ARCH.GRANITE_SWITCH: [
MODEL_TENSOR.TOKEN_EMBD,
MODEL_TENSOR.OUTPUT_NORM,
MODEL_TENSOR.OUTPUT,
MODEL_TENSOR.ATTN_NORM,
MODEL_TENSOR.ATTN_QKV,
MODEL_TENSOR.ATTN_Q,
MODEL_TENSOR.ATTN_K,
MODEL_TENSOR.ATTN_V,
MODEL_TENSOR.ATTN_OUT,
MODEL_TENSOR.FFN_NORM,
MODEL_TENSOR.FFN_GATE,
MODEL_TENSOR.FFN_DOWN,
MODEL_TENSOR.FFN_UP,
],
MODEL_ARCH.CHAMELEON: [
MODEL_TENSOR.TOKEN_EMBD,
MODEL_TENSOR.OUTPUT_NORM,
Expand Down
15 changes: 15 additions & 0 deletions gguf-py/gguf/gguf_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,21 @@ def add_residual_scale(self, value: float) -> None:
def add_embedding_scale(self, value: float) -> None:
self.add_float32(Keys.LLM.EMBEDDING_SCALE.format(arch=self.arch), value)

def add_adapter_count(self, count: int) -> None:
self.add_uint32(Keys.Adapters.COUNT.format(arch=self.arch), count)

def add_adapter_token_ids_activate(self, ids: Sequence[int]) -> None:
self.add_array(Keys.Adapters.TOKEN_IDS_ACTIVATE.format(arch=self.arch), ids)

def add_adapter_token_ids_substitute(self, ids: Sequence[int]) -> None:
self.add_array(Keys.Adapters.TOKEN_IDS_SUBSTITUTE.format(arch=self.arch), ids)

def add_adapter_lora_rank(self, rank: int) -> None:
self.add_uint32(Keys.Adapters.LORA_RANK.format(arch=self.arch), rank)

def add_adapter_router_gain(self, gain: float) -> None:
self.add_float32(Keys.Adapters.ROUTER_GAIN.format(arch=self.arch), gain)

def add_wkv_head_size(self, size: int) -> None:
self.add_uint32(Keys.WKV.HEAD_SIZE.format(arch=self.arch), size)

Expand Down
6 changes: 6 additions & 0 deletions src/llama-arch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
{ LLM_ARCH_GRANITE, "granite" },
{ LLM_ARCH_GRANITE_MOE, "granitemoe" },
{ LLM_ARCH_GRANITE_HYBRID, "granitehybrid" },
{ LLM_ARCH_GRANITE_SWITCH, "graniteswitch" },
{ LLM_ARCH_CHAMELEON, "chameleon" },
{ LLM_ARCH_WAVTOKENIZER_DEC, "wavtokenizer-dec" },
{ LLM_ARCH_PLM, "plm" },
Expand Down Expand Up @@ -217,6 +218,11 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
{ LLM_KV_TIME_DECAY_EXTRA_DIM, "%s.time_decay_extra_dim" },
{ LLM_KV_RESIDUAL_SCALE, "%s.residual_scale" },
{ LLM_KV_EMBEDDING_SCALE, "%s.embedding_scale" },
{ LLM_KV_ADAPTER_COUNT, "%s.adapters.count" },
{ LLM_KV_ADAPTER_TOKEN_IDS_ACTIVATE, "%s.adapters.token_ids_activate" },
{ LLM_KV_ADAPTER_TOKEN_IDS_SUBSTITUTE, "%s.adapters.token_ids_substitute" },
{ LLM_KV_ADAPTER_LORA_RANK, "%s.adapters.lora_rank" },
{ LLM_KV_ADAPTER_ROUTER_GAIN, "%s.adapters.router_gain" },
{ LLM_KV_TOKEN_SHIFT_COUNT, "%s.token_shift_count" },
{ LLM_KV_INTERLEAVE_MOE_LAYER_STEP, "%s.interleave_moe_layer_step" },
{ LLM_KV_FULL_ATTENTION_INTERVAL, "%s.full_attention_interval" },
Expand Down
6 changes: 6 additions & 0 deletions src/llama-arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ enum llm_arch {
LLM_ARCH_GRANITE,
LLM_ARCH_GRANITE_MOE,
LLM_ARCH_GRANITE_HYBRID,
LLM_ARCH_GRANITE_SWITCH,
LLM_ARCH_CHAMELEON,
LLM_ARCH_WAVTOKENIZER_DEC,
LLM_ARCH_PLM,
Expand Down Expand Up @@ -222,6 +223,11 @@ enum llm_kv {
LLM_KV_TIME_DECAY_EXTRA_DIM,
LLM_KV_RESIDUAL_SCALE,
LLM_KV_EMBEDDING_SCALE,
LLM_KV_ADAPTER_COUNT,
LLM_KV_ADAPTER_TOKEN_IDS_ACTIVATE,
LLM_KV_ADAPTER_TOKEN_IDS_SUBSTITUTE,
LLM_KV_ADAPTER_LORA_RANK,
LLM_KV_ADAPTER_ROUTER_GAIN,
LLM_KV_TOKEN_SHIFT_COUNT,
LLM_KV_INTERLEAVE_MOE_LAYER_STEP,
LLM_KV_FULL_ATTENTION_INTERVAL,
Expand Down
3 changes: 2 additions & 1 deletion src/llama-context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3572,8 +3572,9 @@ llama_context * llama_init_from_model(
model->hparams.pooling_type, params.pooling_type);
}

// router_layer >= 0 means n_layer_nextn is repurposed for a router layer, not real MTP
if (params.ctx_type == LLAMA_CONTEXT_TYPE_MTP &&
model->hparams.n_layer_nextn == 0) {
(model->hparams.n_layer_nextn == 0 || model->hparams.router_layer >= 0)) {
LLAMA_LOG_WARN("%s: context type MTP requested but model doesn't contain MTP layers\n", __func__);
return nullptr;
}
Expand Down
10 changes: 10 additions & 0 deletions src/llama-hparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,16 @@ bool llama_hparams::has_kv(uint32_t il) const {
return true;
}

bool llama_hparams::has_rope(uint32_t il) const {
// the router layer stores adapter routing signal, not positional info,
// so it must not be RoPE-shifted
if (router_layer >= 0 && (int32_t) il == router_layer) {
return false;
}

return true;
}

uint32_t llama_hparams::n_layer() const {
return n_layer_all - n_layer_nextn;
}
Expand Down
6 changes: 6 additions & 0 deletions src/llama-hparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ struct llama_hparams {
uint32_t n_embd;
uint32_t n_layer_all;
uint32_t n_layer_nextn = 0;

// granite-switch: index of the single-head "router" KV layer that encodes
// per-token adapter selection. -1 when the model has no such layer.
int32_t router_layer = -1;
uint32_t n_expert = 0;
uint32_t n_expert_used = 0;
uint32_t n_rel_attn_bkts = 0;
Expand Down Expand Up @@ -367,6 +371,8 @@ struct llama_hparams {

bool has_kv(uint32_t il) const;

bool has_rope(uint32_t il) const;

// number of effective layers (excludes nextn layers)
uint32_t n_layer() const;

Expand Down
4 changes: 4 additions & 0 deletions src/llama-kv-cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1925,6 +1925,10 @@ ggml_cgraph * llama_kv_cache::build_graph_shift(llm_graph_result * res, llama_co
for (const auto & layer : layers) {
const uint32_t il = layer.il;

if (!hparams.has_rope(il)) {
continue;
}

const int64_t n_head_kv = hparams.n_head_kv(il);
const int64_t n_embd_k_gqa = hparams.n_embd_k_gqa(il);

Expand Down
24 changes: 12 additions & 12 deletions src/llama-model-loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -919,10 +919,11 @@ static bool weight_buft_supported(const llama_hparams & hparams, ggml_tensor * w
} break;
case GGML_OP_MUL_MAT_ID:
{
const int n_expert_used = hparams.n_expert_used;
GGML_ASSERT(n_expert_used > 0);
ggml_tensor * b = ggml_new_tensor_3d(ctx, GGML_TYPE_F32, w->ne[0], n_expert_used, 512);
ggml_tensor * ids = ggml_new_tensor_2d(ctx, GGML_TYPE_I32, n_expert_used, 512);
// Used for either MoE expert routing or embedded adapter routing
const int n_ids_used = hparams.router_layer >= 0 ? 1 : hparams.n_expert_used;
GGML_ASSERT(n_ids_used > 0);
ggml_tensor * b = ggml_new_tensor_3d(ctx, GGML_TYPE_F32, w->ne[0], n_ids_used, 512);
ggml_tensor * ids = ggml_new_tensor_2d(ctx, GGML_TYPE_I32, n_ids_used, 512);
op_tensor = ggml_mul_mat_id(ctx, w, b, ids);
} break;
case GGML_OP_ADD:
Expand Down Expand Up @@ -1105,15 +1106,14 @@ struct ggml_tensor * llama_model_loader::create_tensor(
return nullptr;
}

// tensors with "bias" suffix are always used with GGML_OP_ADD or GGML_OP_ADD_ID
// tensors with "bias" suffix are always used with GGML_OP_ADD or GGML_OP_ADD_ID;
// embedded-adapter ".lora_a"/".lora_b" tensors are always used with GGML_OP_MUL_MAT_ID
ggml_op op;
bool bias = tn.suffix != nullptr && strcmp(tn.suffix, "bias") == 0;
if (bias) {
if (info.op == GGML_OP_MUL_MAT_ID) {
op = GGML_OP_ADD_ID;
} else {
op = GGML_OP_ADD;
}
if (tn.suffix != nullptr && strcmp(tn.suffix, "bias") == 0) {
op = info.op == GGML_OP_MUL_MAT_ID ? GGML_OP_ADD_ID : GGML_OP_ADD;
} else if (hparams.router_layer >= 0 && tn.suffix != nullptr &&
(strcmp(tn.suffix, "lora_a") == 0 || strcmp(tn.suffix, "lora_b") == 0)) {
op = GGML_OP_MUL_MAT_ID;
} else {
op = info.op;
}
Expand Down
Loading
Loading