Skip to content

Commit a8f6d8a

Browse files
unamedkrclaude
andcommitted
fix(cli): -s seed flag was documented but not implemented
The CLI's --help text has long advertised "-s <seed> Random seed (default: 42)", but tools/quant.c had no parser case for -s and src/engine/tq_generate.c hardcoded `unsigned long long rng_state = 42` in two places. As a result, passing -s 1337 silently fell through to the positional-arg branch, getting parsed as the model path: $ ./build_metal/quant model.gguf -p "..." -n 32 -T 0.7 -s 1337 Loading model from 1337... tq_load_model: cannot open '1337' Failed to load model Discovered while running the cliff-cell seed sweep for the working-memory-cliff tech report (Phase 1B), where 60 of 60 attempted sampled trials degenerated to "model path = <seed>". This commit: 1. Adds `unsigned long long rng_seed` to tq_gen_config_t in both include/turboquant/tq_engine.h and quant.h (the single header used by embedded users via QUANT_IMPLEMENTATION). 2. Initialises rng_seed to 42ULL in tq_default_gen_config (in both src/engine/tq_ops.c and quant.h). 3. Wires rng_state from config->rng_seed in tq_generate (both src/engine/tq_generate.c and quant.h), with a `?: 42ULL` fallback so legacy callers that never set rng_seed get bit-identical behaviour. 4. Adds the -s parser case in tools/quant.c, with `0 -> 42` reserved for "use default" so the documented default is preserved. 5. Adds build_metal/ to .gitignore alongside the existing build_nomt/ and build_nometal/ entries. Backwards compatibility: zero behaviour change for any caller that doesn't pass -s. Same RNG state, same first sampled token, same output bit-for-bit. Verified by running both with and without -s 42 on Llama-3.2-1B-Q8_0 at T=0.7 and confirming identical output. Also verified -s 42 vs -s 1337 produce *different* outputs (the test that was previously impossible). All 35 tests in build_metal/ pass after the change. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f527939 commit a8f6d8a

6 files changed

Lines changed: 19 additions & 4 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ tq_run.dSYM/
6363
docs/assets/hero_backup.png
6464
build_nomt/
6565
build_nometal/
66+
build_metal/
6667

6768
.env
6869

include/turboquant/tq_engine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ typedef struct {
383383
int n_threads;
384384
float rep_penalty; /* repetition penalty (default: 1.1, 1.0 = disabled) */
385385
int rep_window; /* how many recent tokens to penalize (default: 32) */
386+
unsigned long long rng_seed; /* sampling seed (default: 42, 0 = use 42 for back-compat) */
386387
/* KV cache persistence (Document-Level RAG: read once, query forever) */
387388
const char* save_kv_path; /* save KV cache after generation (NULL = don't save) */
388389
const char* load_kv_path; /* load pre-computed KV cache before generation (NULL = normal) */

quant.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,7 @@ typedef struct {
866866
int n_threads;
867867
float rep_penalty; /* repetition penalty (default: 1.1, 1.0 = disabled) */
868868
int rep_window; /* how many recent tokens to penalize (default: 32) */
869+
unsigned long long rng_seed; /* sampling seed (default: 42, 0 = use 42 for back-compat) */
869870
/* Callback for streaming output */
870871
void (*on_token)(const char* text, void* user_data);
871872
void* user_data;
@@ -4129,6 +4130,7 @@ tq_gen_config_t tq_default_gen_config(void) {
41294130
config.n_threads = 1;
41304131
config.rep_penalty = 1.1f;
41314132
config.rep_window = 32;
4133+
config.rng_seed = 42ULL;
41324134
config.on_token = NULL;
41334135
config.user_data = NULL;
41344136
return config;
@@ -15453,9 +15455,11 @@ int tq_generate(tq_model_t* model, tq_tokenizer_t* tokenizer,
1545315455
}
1545415456
}
1545515457

15456-
/* Sample first generated token */
15458+
/* Sample first generated token. The seed is configurable via
15459+
* config->rng_seed (default 42); 0 falls back to 42 so existing
15460+
* callers that never set rng_seed get bit-identical behaviour. */
1545715461
int pos = n_prompt;
15458-
unsigned long long rng_state = 42;
15462+
unsigned long long rng_state = config->rng_seed ? config->rng_seed : 42ULL;
1545915463
int next_token = tq_sample_topp(state->logits, vocab_size,
1546015464
config->temperature, config->top_p,
1546115465
&rng_state);

src/engine/tq_generate.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,12 @@ int tq_generate(tq_model_t* model, tq_tokenizer_t* tokenizer,
359359
}
360360
}
361361

362-
/* Sample first generated token */
362+
/* Sample first generated token. The seed is configurable via
363+
* config->rng_seed to support reproducible sampling sweeps; 0 falls
364+
* back to the historical default of 42 so existing callers that
365+
* never set rng_seed get bit-identical behaviour. */
363366
int pos = pos_after_prefill;
364-
unsigned long long rng_state = 42;
367+
unsigned long long rng_state = config->rng_seed ? config->rng_seed : 42ULL;
365368
int next_token = tq_sample_topp(state->logits, vocab_size,
366369
config->temperature, config->top_p,
367370
&rng_state);

src/engine/tq_ops.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,6 +1682,7 @@ tq_gen_config_t tq_default_gen_config(void) {
16821682
config.n_threads = 1;
16831683
config.rep_penalty = 1.1f;
16841684
config.rep_window = 32;
1685+
config.rng_seed = 42ULL;
16851686
config.on_token = NULL;
16861687
config.user_data = NULL;
16871688
return config;

tools/quant.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ int main(int argc, char** argv) {
199199
const char* kl_baseline_file = NULL;
200200
const char* save_kv_file = NULL; /* --save-kv: save KV cache after generation */
201201
const char* load_kv_file = NULL; /* --load-kv: load pre-computed KV cache */
202+
unsigned long long rng_seed = 42ULL; /* -s <seed>: sampling RNG seed */
202203

203204
for (int i = 1; i < argc; i++) {
204205
if (argv[i][0] != '-') {
@@ -229,6 +230,9 @@ int main(int argc, char** argv) {
229230
}
230231
} else if (strcmp(argv[i], "-j") == 0 && i + 1 < argc) {
231232
n_threads = atoi(argv[++i]);
233+
} else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
234+
rng_seed = strtoull(argv[++i], NULL, 10);
235+
if (rng_seed == 0ULL) rng_seed = 42ULL; /* 0 reserved for "use default" */
232236
} else if (strcmp(argv[i], "-q") == 0) {
233237
if (i + 1 < argc && argv[i + 1][0] != '-') {
234238
const char* qarg = argv[++i];
@@ -1265,6 +1269,7 @@ int main(int argc, char** argv) {
12651269
config.k_highres_window = k_highres_window;
12661270
config.save_kv_path = save_kv_file;
12671271
config.load_kv_path = load_kv_file;
1272+
config.rng_seed = rng_seed;
12681273
config.on_token = print_token;
12691274
config.user_data = NULL;
12701275

0 commit comments

Comments
 (0)