HelloSun commited on
Commit
b0a3013
·
verified ·
1 Parent(s): 47bbe99

Upload pangu-diffusion-llamacpp.patch with huggingface_hub

Browse files
Files changed (1) hide show
  1. pangu-diffusion-llamacpp.patch +349 -107
pangu-diffusion-llamacpp.patch CHANGED
@@ -1,8 +1,8 @@
1
  diff --git a/common/arg.cpp b/common/arg.cpp
2
- index 8b302d5..9c2c761 100644
3
  --- a/common/arg.cpp
4
  +++ b/common/arg.cpp
5
- @@ -4522,6 +4522,16 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
6
  string_format("llada block length for generation (default: %d)", params.diffusion.block_length),
7
  [](common_params & params, int value) { params.diffusion.block_length = value; }
8
  ).set_examples({ LLAMA_EXAMPLE_DIFFUSION }));
@@ -15,15 +15,20 @@ index 8b302d5..9c2c761 100644
15
  + {"--diffusion-confidence-threshold"}, "F",
16
  + string_format("context-causal block diffusion: reveal every masked token above this confidence each step, 0 = one token per step (default: %.2f)", (double) params.diffusion.confidence_threshold),
17
  + [](common_params & params, const std::string & value) { params.diffusion.confidence_threshold = std::stof(value); }
 
 
 
 
 
18
  + ).set_examples({ LLAMA_EXAMPLE_DIFFUSION }));
19
  add_opt(common_arg(
20
  {"--diffusion-cfg-scale"}, "F",
21
  string_format("llada classifier-free guidance scale (default: %.3f)", (double) params.diffusion.cfg_scale),
22
  diff --git a/common/common.h b/common/common.h
23
- index 0c2d5e4..de32160 100644
24
  --- a/common/common.h
25
  +++ b/common/common.h
26
- @@ -413,11 +413,13 @@ struct common_params_diffusion {
27
 
28
  float eps = 0; // epsilon for timesteps
29
  int32_t block_length = 0; // block length for generation
@@ -34,22 +39,23 @@ index 0c2d5e4..de32160 100644
34
 
35
  float cfg_scale = 0; // classifier-free guidance scale
36
  + float confidence_threshold = 0; // context-causal block diffusion: reveal all tokens above this confidence per step (>0 enables it)
 
37
  bool add_gumbel_noise = false; // add gumbel noise to the logits if temp > 0.0
38
  };
39
 
40
  diff --git a/conversion/pangu.py b/conversion/pangu.py
41
- index 74c7653..6b00cec 100644
42
  --- a/conversion/pangu.py
43
  +++ b/conversion/pangu.py
44
- @@ -1,6 +1,7 @@
45
  from __future__ import annotations
46
 
47
- import json
48
  +import os
49
 
50
  from typing import Iterable, TYPE_CHECKING
51
 
52
- @@ -15,6 +16,16 @@ from .base import ModelBase, TextModel, gguf, logger
53
  class PanguEmbeddedModel(TextModel):
54
  model_arch = gguf.MODEL_ARCH.PANGU_EMBED
55
 
@@ -66,7 +72,21 @@ index 74c7653..6b00cec 100644
66
  def set_vocab(self):
67
  self._set_vocab_sentencepiece()
68
 
69
- @@ -39,6 +50,18 @@ class PanguEmbeddedModel(TextModel):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  self.gguf_writer.add_key_length(rope_dim)
71
  self.gguf_writer.add_value_length(rope_dim)
72
 
@@ -86,10 +106,80 @@ index 74c7653..6b00cec 100644
86
  if name == "lm_head.weight":
87
  if self.hparams.get("tie_word_embeddings", False):
88
  diff --git a/examples/diffusion/diffusion-cli.cpp b/examples/diffusion/diffusion-cli.cpp
89
- index c646113..cdcd6f9 100644
90
  --- a/examples/diffusion/diffusion-cli.cpp
91
  +++ b/examples/diffusion/diffusion-cli.cpp
92
- @@ -186,10 +186,21 @@ int main(int argc, char ** argv) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  diff_params.shift_logits = true;
94
  }
95
 
@@ -114,29 +204,84 @@ index c646113..cdcd6f9 100644
114
  diff_params.schedule = DIFFUSION_TRANSFER_SCHEDULE_TIMESTEP_BASED;
115
  diff_params.eps = params.diffusion.eps;
116
  } else if (params.diffusion.block_length) {
117
- @@ -242,6 +253,9 @@ int main(int argc, char ** argv) {
 
 
 
 
 
 
 
 
 
 
118
  LOG_INF("diffusion_params: - %-25s u32 = %d\n", "block_length", diff_params.block_length);
119
  LOG_INF("diffusion_params: - %-25s f32 = %.3f\n", "cfg_scale", diff_params.cfg_scale);
120
  }
121
  + LOG_INF("diffusion_params: - %-25s bool = %s\n", "block_attention", diff_params.block_attention ? "true" : "false");
122
  + LOG_INF("diffusion_params: - %-25s u32 = %d\n", "small_block_length", diff_params.small_block_length);
123
  + LOG_INF("diffusion_params: - %-25s f32 = %.3f\n", "confidence_threshold", diff_params.confidence_threshold);
 
 
 
 
 
 
 
 
 
124
 
125
- diffusion_generate(ctx, input_tokens.data(), output_tokens.data(), n_input, diff_params, n_generated);
126
-
127
- @@ -251,6 +265,7 @@ int main(int argc, char ** argv) {
128
- LOG_INF("\033[2J\033[H");
 
 
 
 
 
 
129
  }
 
130
 
131
- + output_tokens.resize(n_generated);
132
- output_tokens.erase(output_tokens.begin(), output_tokens.begin() + n_input);
133
- std::string output_data = common_detokenize(vocab, output_tokens, false);
134
- LOG_INF("\n%s\n", output_data.c_str());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  diff --git a/examples/diffusion/diffusion.cpp b/examples/diffusion/diffusion.cpp
136
- index 0ff57d9..adc6857 100644
137
  --- a/examples/diffusion/diffusion.cpp
138
  +++ b/examples/diffusion/diffusion.cpp
139
- @@ -102,6 +102,250 @@ static std::vector<int32_t> get_num_transfer_tokens(int32_t mask_count, int32_t
140
  return num_transfer_tokens;
141
  }
142
 
@@ -147,29 +292,42 @@ index 0ff57d9..adc6857 100644
147
  +// block may be split into "small blocks" that are revealed from left to right
148
  +// - after denoising, the block is committed with a causal forward so that the KV cache
149
  +// holds the correct causal context for the following blocks
150
- +static void diffusion_generate_block_attention(llama_context * ctx,
151
- + const llama_token * input_tokens,
152
- + llama_token * output_tokens,
153
- + int32_t n_input,
154
- + const diffusion_params & params,
155
- + int32_t & n_generated) {
 
 
 
156
  + const llama_model * model = llama_get_model(ctx);
157
  + const llama_vocab * vocab = llama_model_get_vocab(model);
158
  + const int32_t n_vocab = llama_vocab_n_tokens(vocab);
159
  + const int32_t B = params.block_length;
 
160
  +
161
- + std::copy(input_tokens, input_tokens + n_input, output_tokens);
162
- + std::fill(output_tokens + n_input, output_tokens + params.max_length, params.mask_token_id);
 
 
 
 
 
 
 
 
 
163
  +
164
  + if (B <= 0) {
165
  + LOG_ERR("%s: block_length must be > 0 for block attention\n", __func__);
166
  + return;
167
  + }
168
  +
169
- + const int32_t n_blocks = (params.max_length - n_input) / B;
170
  + if (n_blocks <= 0) {
171
  + LOG_ERR("%s: max_length (%d) - n_input (%d) is smaller than block_length (%d)\n",
172
- + __func__, params.max_length, n_input, B);
173
  + return;
174
  + }
175
  +
@@ -204,10 +362,13 @@ index 0ff57d9..adc6857 100644
204
  + std::vector<llama_token> step_tok;
205
  + std::vector<float> step_conf;
206
  +
207
- + auto run_range = [&](int32_t p0, int32_t p1) -> const float * {
208
  + batch.clear();
209
- + for (int32_t p = p0; p < p1; ++p) {
210
- + batch.add(output_tokens[p], p, 0, true);
 
 
 
211
  + }
212
  + if (llama_process(ctx, LLAMA_PROCESS_TYPE_DECODE, batch.get()) != 0) {
213
  + LOG_ERR("%s: llama_process failed\n", __func__);
@@ -258,36 +419,58 @@ index 0ff57d9..adc6857 100644
258
  + // 1) prefill the prompt causally and sample the first token of the first block
259
  + if (n_input > 0) {
260
  + llama_set_causal_attn(ctx, true);
261
- + const float * logits = run_range(0, n_input);
262
  + if (!logits) {
263
  + llama_sampler_free(sampler);
264
  + return;
265
  + }
266
- + // only the last prompt position requested logits, so logits[0] is the last prompt position
267
- + llama_token tok;
268
- + float conf;
269
- + sample_from(logits, tok, conf);
270
- + output_tokens[n_input] = tok;
 
 
271
  + }
272
  +
273
  + const llama_token eos = llama_vocab_eos(vocab);
274
  +
 
 
275
  + for (int32_t b = 0; b < n_blocks; ++b) {
 
 
 
 
 
 
 
 
 
 
 
276
  + const int32_t block_start = n_input + b * B;
277
  + const int32_t block_end = block_start + B;
 
278
  +
279
  + // 2) denoise the block with full attention over the cached context + the block itself
280
  + llama_set_causal_attn(ctx, false);
281
  +
282
- + for (int32_t s = 0; s < B; s += small) {
283
- + const int32_t sub_start = block_start + s;
284
  + const int32_t sub_end = std::min(sub_start + small, block_end);
285
  + while (true) {
286
  + bool any_mask = false;
287
- + for (int32_t p = sub_start; p < sub_end; ++p) {
288
- + if (output_tokens[p] == params.mask_token_id) {
289
- + any_mask = true;
290
- + break;
 
 
 
 
 
 
291
  + }
292
  + }
293
  + if (!any_mask) {
@@ -295,54 +478,64 @@ index 0ff57d9..adc6857 100644
295
  + }
296
  +
297
  + // the block is re-decoded on every step; drop its stale keys first
298
- + llama_memory_seq_rm(mem, 0, block_start, -1);
299
- + const float * logits = run_range(block_start, block_end);
 
 
300
  + if (!logits) {
301
  + llama_sampler_free(sampler);
302
  + return;
303
  + }
304
  +
305
- + // predict every masked position of the current small block; reveal the most
306
- + // confident one and, when a confidence threshold is given, every position whose
307
- + // confidence exceeds it (this is what makes the "confidence_threshold" mode fast)
308
- + float best_conf = -INFINITY;
309
- + int32_t best_pos = -1;
310
- + llama_token best_tok = 0;
311
- +
312
- + step_pos.clear();
313
- + step_tok.clear();
314
- + step_conf.clear();
315
- +
316
- + for (int32_t p = sub_start; p < sub_end; ++p) {
317
- + if (output_tokens[p] != params.mask_token_id) {
318
  + continue;
319
  + }
320
- + const int32_t idx = p - block_start;
321
- + const float * lp = (idx == 0) ? logits : logits + (idx - 1) * n_vocab;
322
- + llama_token tok;
323
- + float conf;
324
- + sample_from(lp, tok, conf);
325
- +
326
- + step_pos.push_back(p);
327
- + step_tok.push_back(tok);
328
- + step_conf.push_back(conf);
329
- +
330
- + if (conf > best_conf) {
331
- + best_conf = conf;
332
- + best_pos = p;
333
- + best_tok = tok;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  + }
335
- + }
336
  +
337
- + if (params.confidence_threshold > 0.0f) {
338
- + for (size_t i = 0; i < step_pos.size(); ++i) {
339
- + if (step_conf[i] > params.confidence_threshold) {
340
- + output_tokens[step_pos[i]] = step_tok[i];
 
341
  + }
342
  + }
343
- + }
344
- + if (best_pos >= 0 && output_tokens[best_pos] == params.mask_token_id) {
345
- + output_tokens[best_pos] = best_tok;
346
  + }
347
  + }
348
  + }
@@ -350,49 +543,83 @@ index 0ff57d9..adc6857 100644
350
  + // 3) commit the block with a causal forward (fills the KV cache) and sample
351
  + // the first token of the next block from the last position of this block
352
  + llama_set_causal_attn(ctx, true);
353
- + llama_memory_seq_rm(mem, 0, block_start, -1);
354
- + const float * logits = run_range(block_start, block_end);
 
 
355
  + if (!logits) {
356
  + llama_sampler_free(sampler);
357
  + return;
358
  + }
359
  +
360
- + bool hit_eos = false;
361
- + for (int32_t p = block_start; p < block_end; ++p) {
362
- + if (output_tokens[p] == eos) {
363
- + hit_eos = true;
364
  + }
365
- + }
366
  +
367
- + n_generated = block_end;
 
 
 
 
 
368
  +
369
- + if (hit_eos) {
370
- + break;
371
- + }
 
 
 
372
  +
373
- + if (b < n_blocks - 1) {
374
- + // shifted logits: the token at block_end is predicted from the last block position
375
- + const float * lp = logits + (B - 1) * n_vocab;
376
- + llama_token tok;
377
- + float conf;
378
- + sample_from(lp, tok, conf);
379
- + output_tokens[block_end] = tok;
380
- + n_generated = block_end + 1;
 
381
  + }
382
  + }
383
  +
384
  + llama_sampler_free(sampler);
385
  +}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  +
387
  void diffusion_generate(llama_context * ctx,
388
  const llama_token * input_tokens,
389
  llama_token * output_tokens,
390
- @@ -113,6 +357,11 @@ void diffusion_generate(llama_context * ctx,
391
  return;
392
  }
393
 
394
  + if (params.block_attention) {
395
- + diffusion_generate_block_attention(ctx, input_tokens, output_tokens, n_input, params, n_generated);
 
 
396
  + return;
397
  + }
398
  +
@@ -400,19 +627,34 @@ index 0ff57d9..adc6857 100644
400
 
401
  // Initialize with input and pad with mask tokens
402
  diff --git a/examples/diffusion/diffusion.h b/examples/diffusion/diffusion.h
403
- index 7831445..5cd4c58 100644
404
  --- a/examples/diffusion/diffusion.h
405
  +++ b/examples/diffusion/diffusion.h
406
- @@ -43,6 +43,9 @@ struct diffusion_params {
407
  float cfg_scale = 0.; // Config scale for classifier-free guidance
408
  float eps = 0.; // Timestep scheduling
409
  int32_t block_length = 0; // Block size (for block scheduling)
410
  + int32_t small_block_length = 0; // Small block size inside a block (context-causal block diffusion)
411
  + bool block_attention = false; // Context-causal block diffusion (openPangu-R-Diffusion)
412
  + float confidence_threshold = 0.; // Reveal all masked tokens above this confidence per step (0 = one token per step)
 
413
  float alg_temp = 0; // algorithm temperature (0.0 = deterministic)
414
  bool add_gumbel_noise = false; // Add gumbel noise to the logits if temp > 0.0
415
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
  diff --git a/gguf-py/gguf/constants.py b/gguf-py/gguf/constants.py
417
  index 32e088d..85a6517 100644
418
  --- a/gguf-py/gguf/constants.py
 
1
  diff --git a/common/arg.cpp b/common/arg.cpp
2
+ index 8b302d5..3fe1ffc 100644
3
  --- a/common/arg.cpp
4
  +++ b/common/arg.cpp
5
+ @@ -4522,6 +4522,21 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
6
  string_format("llada block length for generation (default: %d)", params.diffusion.block_length),
7
  [](common_params & params, int value) { params.diffusion.block_length = value; }
8
  ).set_examples({ LLAMA_EXAMPLE_DIFFUSION }));
 
15
  + {"--diffusion-confidence-threshold"}, "F",
16
  + string_format("context-causal block diffusion: reveal every masked token above this confidence each step, 0 = one token per step (default: %.2f)", (double) params.diffusion.confidence_threshold),
17
  + [](common_params & params, const std::string & value) { params.diffusion.confidence_threshold = std::stof(value); }
18
+ + ).set_examples({ LLAMA_EXAMPLE_DIFFUSION }));
19
+ + add_opt(common_arg(
20
+ + {"--diffusion-n-seq"}, "N",
21
+ + string_format("number of parallel sequences to decode together (context-causal block diffusion; the prompt is replicated N times) (default: %d)", params.diffusion.n_seq),
22
+ + [](common_params & params, int value) { params.diffusion.n_seq = value; }
23
  + ).set_examples({ LLAMA_EXAMPLE_DIFFUSION }));
24
  add_opt(common_arg(
25
  {"--diffusion-cfg-scale"}, "F",
26
  string_format("llada classifier-free guidance scale (default: %.3f)", (double) params.diffusion.cfg_scale),
27
  diff --git a/common/common.h b/common/common.h
28
+ index 0c2d5e4..8fe16c3 100644
29
  --- a/common/common.h
30
  +++ b/common/common.h
31
+ @@ -413,11 +413,14 @@ struct common_params_diffusion {
32
 
33
  float eps = 0; // epsilon for timesteps
34
  int32_t block_length = 0; // block length for generation
 
39
 
40
  float cfg_scale = 0; // classifier-free guidance scale
41
  + float confidence_threshold = 0; // context-causal block diffusion: reveal all tokens above this confidence per step (>0 enables it)
42
+ + int32_t n_seq = 1; // number of parallel sequences for context-causal block diffusion
43
  bool add_gumbel_noise = false; // add gumbel noise to the logits if temp > 0.0
44
  };
45
 
46
  diff --git a/conversion/pangu.py b/conversion/pangu.py
47
+ index 74c7653..80ee931 100644
48
  --- a/conversion/pangu.py
49
  +++ b/conversion/pangu.py
50
+ @@ -1,6 +1,6 @@
51
  from __future__ import annotations
52
 
53
+ -import json
54
  +import os
55
 
56
  from typing import Iterable, TYPE_CHECKING
57
 
58
+ @@ -15,15 +15,24 @@ from .base import ModelBase, TextModel, gguf, logger
59
  class PanguEmbeddedModel(TextModel):
60
  model_arch = gguf.MODEL_ARCH.PANGU_EMBED
61
 
 
72
  def set_vocab(self):
73
  self._set_vocab_sentencepiece()
74
 
75
+ - tokenizer_config_file = self.dir_model / 'tokenizer_config.json'
76
+ - if tokenizer_config_file.is_file():
77
+ - with open(tokenizer_config_file, "r", encoding="utf-8") as f:
78
+ - tokenizer_config_json = json.load(f)
79
+ - if "add_prefix_space" in tokenizer_config_json:
80
+ - self.gguf_writer.add_add_space_prefix(tokenizer_config_json["add_prefix_space"])
81
+ + # The PanguTokenizer (slow sentencepiece, `legacy: true`) does not actually prepend a
82
+ + # space to the input; the `add_prefix_space` flag in tokenizer_config.json is not
83
+ + # honored by the reference tokenizer. Using it makes llama.cpp insert spurious space
84
+ + # tokens after every special token (verified against the HF tokenizer). Force it off.
85
+ + self.gguf_writer.add_add_space_prefix(False)
86
+
87
+ def set_gguf_parameters(self):
88
+ super().set_gguf_parameters()
89
+ @@ -39,6 +48,18 @@ class PanguEmbeddedModel(TextModel):
90
  self.gguf_writer.add_key_length(rope_dim)
91
  self.gguf_writer.add_value_length(rope_dim)
92
 
 
106
  if name == "lm_head.weight":
107
  if self.hparams.get("tie_word_embeddings", False):
108
  diff --git a/examples/diffusion/diffusion-cli.cpp b/examples/diffusion/diffusion-cli.cpp
109
+ index c646113..7bdae90 100644
110
  --- a/examples/diffusion/diffusion-cli.cpp
111
  +++ b/examples/diffusion/diffusion-cli.cpp
112
+ @@ -132,10 +132,38 @@ int main(int argc, char ** argv) {
113
+ return 1;
114
+ }
115
+
116
+ + const int32_t n_seq = std::max(1, params.diffusion.n_seq);
117
+ + const int32_t block_len = std::max(1, params.diffusion.block_length);
118
+ + const int32_t max_length = params.n_ubatch;
119
+ +
120
+ + const llama_vocab * vocab = llama_model_get_vocab(model);
121
+ +
122
+ + std::string formatted_prompt = format_input_text(params.prompt, params.system_prompt, params.enable_chat_template, model);
123
+ +
124
+ + std::vector<llama_token> input_tokens = common_tokenize(vocab,
125
+ + formatted_prompt,
126
+ + /*add special tokens*/ true,
127
+ + /*parse special*/ true);
128
+ +
129
+ + int n_input = input_tokens.size();
130
+ +
131
+ + if (params.diffusion.confidence_threshold > 0.0f || n_seq > 1) {
132
+ + if (n_seq * block_len > (int32_t) params.n_ubatch) {
133
+ + LOG_ERR("error: --diffusion-n-seq %d x block length %d (%d) exceeds -ub %d; increase -ub to at least %d\n",
134
+ + n_seq, block_len, n_seq * block_len, params.n_ubatch, n_seq * block_len);
135
+ + llama_model_free(model);
136
+ + return 1;
137
+ + }
138
+ + }
139
+ +
140
+ + // the prefill and the block forward of all sequences must fit in a single micro-batch
141
+ + const uint32_t need_ubatch = (uint32_t) std::max<int64_t>({ (int64_t) n_seq * block_len, (int64_t) n_seq * n_input, (int64_t) block_len });
142
+ +
143
+ llama_context_params ctx_params = llama_context_default_params();
144
+ - ctx_params.n_ctx = params.n_ctx;
145
+ - ctx_params.n_batch = params.n_batch;
146
+ - ctx_params.n_ubatch = params.n_ubatch;
147
+ + ctx_params.n_ctx = std::max<uint32_t>(params.n_ctx, (uint32_t) n_seq * max_length);
148
+ + ctx_params.n_batch = std::max<uint32_t>(params.n_batch, need_ubatch);
149
+ + ctx_params.n_ubatch = std::max<uint32_t>(params.n_ubatch, need_ubatch);
150
+ + ctx_params.n_seq_max = std::max<uint32_t>(ctx_params.n_seq_max, n_seq);
151
+ ctx_params.flash_attn_type = params.flash_attn_type;
152
+ ctx_params.no_perf = params.no_perf;
153
+ ctx_params.type_k = params.cache_type_k;
154
+ @@ -150,17 +178,6 @@ int main(int argc, char ** argv) {
155
+
156
+ llama_set_n_threads(ctx, params.cpuparams.n_threads, params.cpuparams_batch.n_threads);
157
+
158
+ - const llama_vocab * vocab = llama_model_get_vocab(model);
159
+ -
160
+ - std::string formatted_prompt = format_input_text(params.prompt, params.system_prompt, params.enable_chat_template, model);
161
+ -
162
+ - std::vector<llama_token> input_tokens = common_tokenize(vocab,
163
+ - formatted_prompt,
164
+ - /*add special tokens*/ true,
165
+ - /*parse special*/ true);
166
+ -
167
+ - int n_input = input_tokens.size();
168
+ -
169
+ if (static_cast<uint32_t>(n_input) >= llama_n_ctx(ctx)) {
170
+ LOG_ERR("error: input too long (%d tokens), max context is %d\n", n_input, llama_n_ctx(ctx));
171
+ llama_free(ctx);
172
+ @@ -175,7 +192,8 @@ int main(int argc, char ** argv) {
173
+ bool visual_mode = params.diffusion.visual_mode;
174
+
175
+ int32_t n_generated = 0;
176
+ - std::vector<llama_token> output_tokens(params.n_ubatch);
177
+ + std::vector<llama_token> output_tokens((size_t) n_seq * max_length);
178
+ + std::vector<int32_t> n_generated_vec(n_seq, 0);
179
+
180
+ struct diffusion_params diff_params;
181
+
182
+ @@ -186,10 +204,21 @@ int main(int argc, char ** argv) {
183
  diff_params.shift_logits = true;
184
  }
185
 
 
204
  diff_params.schedule = DIFFUSION_TRANSFER_SCHEDULE_TIMESTEP_BASED;
205
  diff_params.eps = params.diffusion.eps;
206
  } else if (params.diffusion.block_length) {
207
+ @@ -202,7 +231,8 @@ int main(int argc, char ** argv) {
208
+ diff_params.temperature = params.sampling.temp;
209
+ diff_params.steps = params.diffusion.steps;
210
+ diff_params.algorithm = static_cast<diffusion_algorithm>(params.diffusion.algorithm);
211
+ - diff_params.max_length = params.n_ubatch;
212
+ + diff_params.max_length = max_length;
213
+ + diff_params.n_seq = n_seq;
214
+ diff_params.top_p = params.sampling.top_p;
215
+ diff_params.top_k = params.sampling.top_k;
216
+ diff_params.visual_mode = params.diffusion.visual_mode;
217
+ @@ -242,20 +272,52 @@ int main(int argc, char ** argv) {
218
  LOG_INF("diffusion_params: - %-25s u32 = %d\n", "block_length", diff_params.block_length);
219
  LOG_INF("diffusion_params: - %-25s f32 = %.3f\n", "cfg_scale", diff_params.cfg_scale);
220
  }
221
  + LOG_INF("diffusion_params: - %-25s bool = %s\n", "block_attention", diff_params.block_attention ? "true" : "false");
222
  + LOG_INF("diffusion_params: - %-25s u32 = %d\n", "small_block_length", diff_params.small_block_length);
223
  + LOG_INF("diffusion_params: - %-25s f32 = %.3f\n", "confidence_threshold", diff_params.confidence_threshold);
224
+ + LOG_INF("diffusion_params: - %-25s u32 = %d\n", "n_seq", diff_params.n_seq);
225
+ +
226
+ + if (diff_params.n_seq > 1 && diff_params.block_attention) {
227
+ + diffusion_generate_nseq(ctx, input_tokens.data(), n_input, diff_params.n_seq,
228
+ + output_tokens.data(), diff_params, n_generated_vec.data());
229
+ + } else {
230
+ + diffusion_generate(ctx, input_tokens.data(), output_tokens.data(), n_input, diff_params, n_generated);
231
+ + n_generated_vec[0] = n_generated;
232
+ + }
233
 
234
+ - diffusion_generate(ctx, input_tokens.data(), output_tokens.data(), n_input, diff_params, n_generated);
235
+ -
236
+ - if (n_generated > 0) {
237
+ - if (visual_mode) {
238
+ - //clear screen and move cursor to top-left
239
+ - LOG_INF("\033[2J\033[H");
240
+ + bool any_generated = false;
241
+ + for (int32_t s = 0; s < n_seq; ++s) {
242
+ + if (n_generated_vec[s] > 0) {
243
+ + any_generated = true;
244
  }
245
+ + }
246
 
247
+ - output_tokens.erase(output_tokens.begin(), output_tokens.begin() + n_input);
248
+ - std::string output_data = common_detokenize(vocab, output_tokens, false);
249
+ - LOG_INF("\n%s\n", output_data.c_str());
250
+ - } else {
251
+ + if (!any_generated) {
252
+ LOG_INF("Error: diffusion generation failed\n");
253
+ + llama_free(ctx);
254
+ + llama_model_free(model);
255
+ + llama_backend_free();
256
+ + return 0;
257
+ + }
258
+ +
259
+ + if (visual_mode) {
260
+ + //clear screen and move cursor to top-left
261
+ + LOG_INF("\033[2J\033[H");
262
+ + }
263
+ +
264
+ + for (int32_t s = 0; s < n_seq; ++s) {
265
+ + if (n_generated_vec[s] <= n_input) {
266
+ + continue;
267
+ + }
268
+ + std::vector<llama_token> seq_tokens(output_tokens.begin() + (size_t) s * max_length,
269
+ + output_tokens.begin() + (size_t) s * max_length + n_generated_vec[s]);
270
+ + seq_tokens.erase(seq_tokens.begin(), seq_tokens.begin() + n_input);
271
+ + std::string output_data = common_detokenize(vocab, seq_tokens, false);
272
+ + if (n_seq > 1) {
273
+ + LOG_INF("\n===== sequence %d =====\n%s\n", s, output_data.c_str());
274
+ + } else {
275
+ + LOG_INF("\n%s\n", output_data.c_str());
276
+ + }
277
+ }
278
+
279
+ llama_free(ctx);
280
  diff --git a/examples/diffusion/diffusion.cpp b/examples/diffusion/diffusion.cpp
281
+ index 0ff57d9..23338bf 100644
282
  --- a/examples/diffusion/diffusion.cpp
283
  +++ b/examples/diffusion/diffusion.cpp
284
+ @@ -102,6 +102,330 @@ static std::vector<int32_t> get_num_transfer_tokens(int32_t mask_count, int32_t
285
  return num_transfer_tokens;
286
  }
287
 
 
292
  +// block may be split into "small blocks" that are revealed from left to right
293
  +// - after denoising, the block is committed with a causal forward so that the KV cache
294
  +// holds the correct causal context for the following blocks
295
+ +//
296
+ +// Supports n_seq parallel sequences decoded in lockstep (same prompt length / block grid).
297
+ +static void diffusion_generate_block_attention_impl(llama_context * ctx,
298
+ + const llama_token * input_tokens,
299
+ + int32_t n_input,
300
+ + int32_t n_seq,
301
+ + llama_token * output_tokens,
302
+ + const diffusion_params & params,
303
+ + std::vector<int32_t> & n_generated) {
304
  + const llama_model * model = llama_get_model(ctx);
305
  + const llama_vocab * vocab = llama_model_get_vocab(model);
306
  + const int32_t n_vocab = llama_vocab_n_tokens(vocab);
307
  + const int32_t B = params.block_length;
308
+ + const int32_t ml = params.max_length;
309
  +
310
+ + n_generated.assign(n_seq, 0);
311
+ +
312
+ + auto row_of = [&](int32_t s) -> llama_token * {
313
+ + return output_tokens + (size_t) s * ml;
314
+ + };
315
+ +
316
+ + for (int32_t s = 0; s < n_seq; ++s) {
317
+ + llama_token * row = row_of(s);
318
+ + std::copy(input_tokens, input_tokens + n_input, row);
319
+ + std::fill(row + n_input, row + ml, params.mask_token_id);
320
+ + }
321
  +
322
  + if (B <= 0) {
323
  + LOG_ERR("%s: block_length must be > 0 for block attention\n", __func__);
324
  + return;
325
  + }
326
  +
327
+ + const int32_t n_blocks = (ml - n_input) / B;
328
  + if (n_blocks <= 0) {
329
  + LOG_ERR("%s: max_length (%d) - n_input (%d) is smaller than block_length (%d)\n",
330
+ + __func__, ml, n_input, B);
331
  + return;
332
  + }
333
  +
 
362
  + std::vector<llama_token> step_tok;
363
  + std::vector<float> step_conf;
364
  +
365
+ + auto run_range = [&](int32_t p0, int32_t p1, bool all_outputs) -> const float * {
366
  + batch.clear();
367
+ + for (int32_t s = 0; s < n_seq; ++s) {
368
+ + llama_token * row = row_of(s);
369
+ + for (int32_t p = p0; p < p1; ++p) {
370
+ + batch.add(row[p], p, s, all_outputs || (p == p1 - 1));
371
+ + }
372
  + }
373
  + if (llama_process(ctx, LLAMA_PROCESS_TYPE_DECODE, batch.get()) != 0) {
374
  + LOG_ERR("%s: llama_process failed\n", __func__);
 
419
  + // 1) prefill the prompt causally and sample the first token of the first block
420
  + if (n_input > 0) {
421
  + llama_set_causal_attn(ctx, true);
422
+ + const float * logits = run_range(0, n_input, true);
423
  + if (!logits) {
424
  + llama_sampler_free(sampler);
425
  + return;
426
  + }
427
+ + // only the last prompt position of each sequence seeds the first block
428
+ + for (int32_t s = 0; s < n_seq; ++s) {
429
+ + llama_token tok;
430
+ + float conf;
431
+ + sample_from(logits + (size_t)(s * n_input + (n_input - 1)) * n_vocab, tok, conf);
432
+ + row_of(s)[n_input] = tok;
433
+ + }
434
  + }
435
  +
436
  + const llama_token eos = llama_vocab_eos(vocab);
437
  +
438
+ + std::vector<char> finished(n_seq, 0);
439
+ +
440
  + for (int32_t b = 0; b < n_blocks; ++b) {
441
+ + bool all_finished = true;
442
+ + for (int32_t s = 0; s < n_seq; ++s) {
443
+ + if (!finished[s]) {
444
+ + all_finished = false;
445
+ + break;
446
+ + }
447
+ + }
448
+ + if (all_finished) {
449
+ + break;
450
+ + }
451
+ +
452
  + const int32_t block_start = n_input + b * B;
453
  + const int32_t block_end = block_start + B;
454
+ + const int32_t len = B;
455
  +
456
  + // 2) denoise the block with full attention over the cached context + the block itself
457
  + llama_set_causal_attn(ctx, false);
458
  +
459
+ + for (int32_t sb = 0; sb < B; sb += small) {
460
+ + const int32_t sub_start = block_start + sb;
461
  + const int32_t sub_end = std::min(sub_start + small, block_end);
462
  + while (true) {
463
  + bool any_mask = false;
464
+ + for (int32_t s = 0; s < n_seq && !any_mask; ++s) {
465
+ + if (finished[s]) {
466
+ + continue;
467
+ + }
468
+ + const llama_token * row = row_of(s);
469
+ + for (int32_t p = sub_start; p < sub_end; ++p) {
470
+ + if (row[p] == params.mask_token_id) {
471
+ + any_mask = true;
472
+ + break;
473
+ + }
474
  + }
475
  + }
476
  + if (!any_mask) {
 
478
  + }
479
  +
480
  + // the block is re-decoded on every step; drop its stale keys first
481
+ + for (int32_t s = 0; s < n_seq; ++s) {
482
+ + llama_memory_seq_rm(mem, s, block_start, -1);
483
+ + }
484
+ + const float * logits = run_range(block_start, block_end, true);
485
  + if (!logits) {
486
  + llama_sampler_free(sampler);
487
  + return;
488
  + }
489
  +
490
+ + // per sequence: predict every masked position of the current small block; reveal
491
+ + // the most confident one and, when a threshold is given, every position whose
492
+ + // confidence exceeds it (this is what makes "confidence_threshold" fast)
493
+ + for (int32_t s = 0; s < n_seq; ++s) {
494
+ + if (finished[s]) {
 
 
 
 
 
 
 
 
495
  + continue;
496
  + }
497
+ + llama_token * row = row_of(s);
498
+ + const float * base = logits + (size_t) s * len * n_vocab;
499
+ +
500
+ + float best_conf = -INFINITY;
501
+ + int32_t best_pos = -1;
502
+ + llama_token best_tok = 0;
503
+ +
504
+ + step_pos.clear();
505
+ + step_tok.clear();
506
+ + step_conf.clear();
507
+ +
508
+ + for (int32_t p = sub_start; p < sub_end; ++p) {
509
+ + if (row[p] != params.mask_token_id) {
510
+ + continue;
511
+ + }
512
+ + const int32_t idx = p - block_start;
513
+ + const float * lp = (idx == 0) ? base : base + (idx - 1) * n_vocab;
514
+ + llama_token tok;
515
+ + float conf;
516
+ + sample_from(lp, tok, conf);
517
+ +
518
+ + step_pos.push_back(p);
519
+ + step_tok.push_back(tok);
520
+ + step_conf.push_back(conf);
521
+ +
522
+ + if (conf > best_conf) {
523
+ + best_conf = conf;
524
+ + best_pos = p;
525
+ + best_tok = tok;
526
+ + }
527
  + }
 
528
  +
529
+ + if (params.confidence_threshold > 0.0f) {
530
+ + for (size_t i = 0; i < step_pos.size(); ++i) {
531
+ + if (step_conf[i] > params.confidence_threshold) {
532
+ + row[step_pos[i]] = step_tok[i];
533
+ + }
534
  + }
535
  + }
536
+ + if (best_pos >= 0 && row[best_pos] == params.mask_token_id) {
537
+ + row[best_pos] = best_tok;
538
+ + }
539
  + }
540
  + }
541
  + }
 
543
  + // 3) commit the block with a causal forward (fills the KV cache) and sample
544
  + // the first token of the next block from the last position of this block
545
  + llama_set_causal_attn(ctx, true);
546
+ + for (int32_t s = 0; s < n_seq; ++s) {
547
+ + llama_memory_seq_rm(mem, s, block_start, -1);
548
+ + }
549
+ + const float * logits = run_range(block_start, block_end, true);
550
  + if (!logits) {
551
  + llama_sampler_free(sampler);
552
  + return;
553
  + }
554
  +
555
+ + for (int32_t s = 0; s < n_seq; ++s) {
556
+ + if (finished[s]) {
557
+ + continue;
 
558
  + }
559
+ + llama_token * row = row_of(s);
560
  +
561
+ + bool hit_eos = false;
562
+ + for (int32_t p = block_start; p < block_end; ++p) {
563
+ + if (row[p] == eos) {
564
+ + hit_eos = true;
565
+ + }
566
+ + }
567
  +
568
+ + n_generated[s] = block_end;
569
+ +
570
+ + if (hit_eos) {
571
+ + finished[s] = 1;
572
+ + continue;
573
+ + }
574
  +
575
+ + if (b < n_blocks - 1) {
576
+ + // shifted logits: the token at block_end is predicted from the last block position
577
+ + const float * lp = logits + (size_t) s * len * n_vocab + (len - 1) * n_vocab;
578
+ + llama_token tok;
579
+ + float conf;
580
+ + sample_from(lp, tok, conf);
581
+ + row[block_end] = tok;
582
+ + n_generated[s] = block_end + 1;
583
+ + }
584
  + }
585
  + }
586
  +
587
  + llama_sampler_free(sampler);
588
  +}
589
+ +
590
+ +void diffusion_generate_nseq(llama_context * ctx,
591
+ + const llama_token * input_tokens,
592
+ + int32_t n_input,
593
+ + int32_t n_seq,
594
+ + llama_token * output_tokens,
595
+ + const diffusion_params & params,
596
+ + int32_t * n_generated) {
597
+ + if (!ctx || !input_tokens || !output_tokens || n_input <= 0 || params.max_length <= n_input || n_seq <= 0) {
598
+ + return;
599
+ + }
600
+ + if (!params.block_attention) {
601
+ + LOG_ERR("%s: only supported for context-causal block diffusion models\n", __func__);
602
+ + return;
603
+ + }
604
+ +
605
+ + std::vector<int32_t> gen;
606
+ + diffusion_generate_block_attention_impl(ctx, input_tokens, n_input, n_seq, output_tokens, params, gen);
607
+ + for (int32_t s = 0; s < n_seq; ++s) {
608
+ + n_generated[s] = gen[s];
609
+ + }
610
+ +}
611
  +
612
  void diffusion_generate(llama_context * ctx,
613
  const llama_token * input_tokens,
614
  llama_token * output_tokens,
615
+ @@ -113,6 +437,13 @@ void diffusion_generate(llama_context * ctx,
616
  return;
617
  }
618
 
619
  + if (params.block_attention) {
620
+ + std::vector<int32_t> gen;
621
+ + diffusion_generate_block_attention_impl(ctx, input_tokens, n_input, 1, output_tokens, params, gen);
622
+ + n_generated = gen.empty() ? 0 : gen[0];
623
  + return;
624
  + }
625
  +
 
627
 
628
  // Initialize with input and pad with mask tokens
629
  diff --git a/examples/diffusion/diffusion.h b/examples/diffusion/diffusion.h
630
+ index 7831445..8af53ac 100644
631
  --- a/examples/diffusion/diffusion.h
632
  +++ b/examples/diffusion/diffusion.h
633
+ @@ -43,6 +43,10 @@ struct diffusion_params {
634
  float cfg_scale = 0.; // Config scale for classifier-free guidance
635
  float eps = 0.; // Timestep scheduling
636
  int32_t block_length = 0; // Block size (for block scheduling)
637
  + int32_t small_block_length = 0; // Small block size inside a block (context-causal block diffusion)
638
  + bool block_attention = false; // Context-causal block diffusion (openPangu-R-Diffusion)
639
  + float confidence_threshold = 0.; // Reveal all masked tokens above this confidence per step (0 = one token per step)
640
+ + int32_t n_seq = 1; // Number of parallel sequences (context-causal block diffusion)
641
  float alg_temp = 0; // algorithm temperature (0.0 = deterministic)
642
  bool add_gumbel_noise = false; // Add gumbel noise to the logits if temp > 0.0
643
 
644
+ @@ -55,3 +59,13 @@ void diffusion_generate(llama_context * ctx,
645
+ int32_t n_input,
646
+ const diffusion_params & params,
647
+ int32_t & n_generated);
648
+ +
649
+ +// batched (multi-sequence) context-causal block diffusion;
650
+ +// output_tokens holds n_seq rows of params.max_length, n_generated has n_seq entries
651
+ +void diffusion_generate_nseq(llama_context * ctx,
652
+ + const llama_token * input_tokens,
653
+ + int32_t n_input,
654
+ + int32_t n_seq,
655
+ + llama_token * output_tokens,
656
+ + const diffusion_params & params,
657
+ + int32_t * n_generated);
658
  diff --git a/gguf-py/gguf/constants.py b/gguf-py/gguf/constants.py
659
  index 32e088d..85a6517 100644
660
  --- a/gguf-py/gguf/constants.py