@@ -478,6 +478,24 @@ def substrate_theme_momentum(recent_tokens: list,
478478 return out / (out .sum () + 1e-8 )
479479
480480
481+ def substrate_vocab_curriculum (probs : torch .Tensor ,
482+ active_vocab_size : int ) -> torch .Tensor :
483+ """Vocabulary expansion curriculum: restrict sampling to the first
484+ `active_vocab_size` tokens. As substrate K shrinks across cycles,
485+ vocab EXPANDS through Fibonacci tiers (F(8)=21 -> F(12)=144 -> full).
486+ Functional/common tokens always available; content/proper nouns
487+ unlock progressively. Pure substrate (Fibonacci-tier walk).
488+ """
489+ if active_vocab_size <= 0 or active_vocab_size >= probs .shape [0 ]:
490+ return probs
491+ out = probs .clone ()
492+ out [active_vocab_size :] = 0.0
493+ s = out .sum ()
494+ if s < 1e-8 :
495+ return probs
496+ return out / s
497+
498+
481499def substrate_golden_phase (t_pos : int , probs : torch .Tensor ,
482500 vocab_size : int ) -> torch .Tensor :
483501 """Golden-angle phase: functional/content rhythm primitive.
@@ -743,7 +761,8 @@ def autoregressive_generate(model, prompt: torch.Tensor, n_new: int,
743761 recency_penalty : bool = True ,
744762 bigram_prior : torch .Tensor = None ,
745763 vocab : list = None ,
746- token_signatures : torch .Tensor = None ):
764+ token_signatures : torch .Tensor = None ,
765+ active_vocab_size : int = None ):
747766 """Sample n_new tokens autoregressively with substrate sampling AND
748767 a substrate-canonical recency penalty.
749768
@@ -796,6 +815,10 @@ def autoregressive_generate(model, prompt: torch.Tensor, n_new: int,
796815 history_aw = seq [0 , - 21 :]
797816 probs [0 ] = substrate_anti_stagnation (history_aw , probs [0 ],
798817 vocab_size )
818+ # Vocab curriculum (applied last; hard mask high-rank).
819+ if active_vocab_size is not None :
820+ probs [0 ] = substrate_vocab_curriculum (
821+ probs [0 ], active_vocab_size )
799822 next_tok = torch .multinomial (probs , num_samples = 1 )
800823 seq = torch .cat ([seq , next_tok ], dim = 1 )
801824 model .train ()
@@ -808,7 +831,8 @@ def _single_stage_refine(model, draft, vocab_size, scorer, mode: str,
808831 patience : int = 5 ,
809832 bigram_prior : torch .Tensor = None ,
810833 vocab : list = None ,
811- token_signatures : torch .Tensor = None ):
834+ token_signatures : torch .Tensor = None ,
835+ active_vocab_size : int = None ):
812836 """One refinement stage: optimize a single score until plateau.
813837
814838 mode: 'min' (harmony, quality) or 'max' (creativity).
@@ -883,6 +907,10 @@ def _single_stage_refine(model, draft, vocab_size, scorer, mode: str,
883907 history_aw = new [0 , aw_start :t_draft ]
884908 pos_probs = substrate_anti_stagnation (
885909 history_aw , pos_probs , vocab_size_local )
910+ # Vocab curriculum (hard mask high-rank).
911+ if active_vocab_size is not None :
912+ pos_probs = substrate_vocab_curriculum (
913+ pos_probs , active_vocab_size )
886914 new [0 , t_draft ] = torch .multinomial (
887915 pos_probs , num_samples = 1 ).item ()
888916
@@ -914,7 +942,8 @@ def staged_refine(model, prompt, n_new, vocab_size,
914942 temperature : float = 0.5 ,
915943 bigram_prior : torch .Tensor = None ,
916944 vocab : list = None ,
917- token_signatures : torch .Tensor = None ):
945+ token_signatures : torch .Tensor = None ,
946+ active_vocab_size : int = None ):
918947 """Staircase refinement: hit one score, then the next, then the next.
919948
920949 Stage 1: substrate alignment (minimize harmony) -- match the shape.
@@ -930,7 +959,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
930959 with torch .no_grad ():
931960 draft = autoregressive_generate (model , prompt , n_new = n_new ,
932961 vocab_size = vocab_size ,
933- temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures )
962+ temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures , active_vocab_size = active_vocab_size )
934963 stages_out = {}
935964 stages_out ["initial" ] = {"seq" : draft .clone (),
936965 "harmony" : harmony_scorer (draft ),
@@ -943,7 +972,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
943972 n_iters = n_iters_per_stage ,
944973 resample_frac = resample_frac ,
945974 prompt_len = prompt_len ,
946- temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures )
975+ temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures , active_vocab_size = active_vocab_size )
947976 stages_out ["after_harmony" ] = {"seq" : draft .clone (),
948977 "trajectory" : h_traj ,
949978 "harmony" : harmony_scorer (draft ),
@@ -956,7 +985,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
956985 n_iters = n_iters_per_stage ,
957986 resample_frac = resample_frac ,
958987 prompt_len = prompt_len ,
959- temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures )
988+ temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures , active_vocab_size = active_vocab_size )
960989 stages_out ["after_quality" ] = {"seq" : draft .clone (),
961990 "trajectory" : q_traj ,
962991 "harmony" : harmony_scorer (draft ),
@@ -970,7 +999,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
970999 n_iters = n_iters_per_stage ,
9711000 resample_frac = resample_frac ,
9721001 prompt_len = prompt_len ,
973- temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures )
1002+ temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures , active_vocab_size = active_vocab_size )
9741003 stages_out ["after_creativity" ] = {"seq" : draft .clone (),
9751004 "trajectory" : c_traj ,
9761005 "harmony" : harmony_scorer (draft ),
@@ -1004,7 +1033,7 @@ def iterative_refine(model, prompt, n_new, vocab_size,
10041033 # Step 1: initial draft.
10051034 draft = autoregressive_generate (model , prompt , n_new = n_new ,
10061035 vocab_size = vocab_size ,
1007- temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures )
1036+ temperature = temperature , bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures , active_vocab_size = active_vocab_size )
10081037 history = []
10091038 h0 = harmony_scorer (draft ) if harmony_scorer is not None else None
10101039 q0 = quality_scorer (draft ) if quality_scorer is not None else None
@@ -1425,9 +1454,18 @@ def quality_fn(seq_tokens):
14251454 global_step = 0
14261455 prompt = train_seed [:16 ].unsqueeze (0 )
14271456
1457+ # Substrate vocab curriculum: tier-walked Fibonacci expansion.
1458+ # Cycle 1: F(8)=21 word slots; cycle 6: full vocab.
1459+ _VOCAB_TIERS = [21 , 34 , 55 , 89 , 144 ] # F(8)..F(12)
1460+ n_chars = sum (1 for t in (vocab or []) if len (t ) == 1 ) if vocab else 65
14281461 for cycle in range (n_cycles ):
1462+ if cycle < len (_VOCAB_TIERS ):
1463+ active_vocab_size = n_chars + _VOCAB_TIERS [cycle ]
1464+ else :
1465+ active_vocab_size = None # full vocab unlocked
14291466 print (f"\n --- Cycle { cycle + 1 } /{ n_cycles } "
1430- f"active_base_size={ active_base .numel ()} chars "
1467+ f"active_base_size={ active_base .numel ()} chars "
1468+ f"active_vocab={ active_vocab_size or vocab_size } "
14311469 f"best_creativity={ best_creativity :.4f} ---" , flush = True )
14321470 for s in range (steps_per_cycle ):
14331471 new_K = sched (global_step , args .steps )
@@ -1471,14 +1509,14 @@ def quality_fn(seq_tokens):
14711509 draft = autoregressive_generate (
14721510 model , prompt_s , n_new = growth_n_new ,
14731511 vocab_size = vocab_size , temperature = 0.8 ,
1474- bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures )
1512+ bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures , active_vocab_size = active_vocab_size )
14751513 refined_s , _ = staged_refine (
14761514 model , prompt_s , n_new = growth_n_new , vocab_size = vocab_size ,
14771515 harmony_scorer = harmony_fn , quality_scorer = quality_fn ,
14781516 creativity_scorer = creativity_fn ,
14791517 n_iters_per_stage = 30 , resample_frac = 0.35 ,
14801518 prompt_len = 16 , temperature = 0.5 ,
1481- bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures )
1519+ bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures , active_vocab_size = active_vocab_size )
14821520 samples .append ((refined_s .squeeze (0 ).clone (),
14831521 creativity_fn (refined_s )))
14841522 # Sort by creativity desc, keep top K.
@@ -1548,14 +1586,14 @@ def quality_fn(seq_tokens):
15481586 final_gen = autoregressive_generate (model , prompt , n_new = n_new ,
15491587 vocab_size = vocab_size ,
15501588 temperature = 0.8 ,
1551- bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures )
1589+ bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures , active_vocab_size = active_vocab_size )
15521590 final_refined , _ = staged_refine (
15531591 model , prompt , n_new = n_new , vocab_size = vocab_size ,
15541592 harmony_scorer = harmony_fn , quality_scorer = quality_fn ,
15551593 creativity_scorer = creativity_fn ,
15561594 n_iters_per_stage = 200 , resample_frac = 0.35 ,
15571595 prompt_len = 16 , temperature = 0.5 ,
1558- bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures )
1596+ bigram_prior = bigram_prior , vocab = vocab , token_signatures = token_signatures , active_vocab_size = active_vocab_size )
15591597
15601598 return {"name" : name , "mode" : "self_distillation" ,
15611599 "n_params" : n_params ,
0 commit comments