theory StarForth_Loop1_Heat imports StarForth_Base begin (* AND/OR/XOR infix notation moved behind an opt-in bundle at some point after 2011 -- unbundled by default now. Same fix as StarForth_Q48_16.thy. *) unbundle bit_operations_syntax (* ========================================================================= StarForth_Loop1_Heat — Execution Heat Tracking (Physics Loop #1) Mirrors: src/dictionary_heat_optimization.c src/word_source/ (heat increment on every word execution) Every time a word executes its de_heat field is incremented. A background decay sweep (Loop #3) decrements heat over time. Words can be FROZEN (heat does not decay) or PINNED (heat cannot reach zero). This theory proves: • Heat is non-negative throughout its lifecycle. • Heat increment strictly increases de_heat. • Heat is bounded above by a MAX_HEAT constant. • FROZEN words are immune to decay. • PINNED words' heat stays ≥ 1 after decay. ======================================================================== *) (* ========================================================================= Section 1: Heat constants ======================================================================== *) (* Initial heat assigned when a word is first defined *) definition HEAT_INIT :: cell where "HEAT_INIT = 0" (* Maximum representable heat (arbitrary upper bound for well-formedness) *) definition HEAT_MAX :: cell where "HEAT_MAX = 1000000" (* Demote from hot-words cache below this threshold *) definition HEAT_DEMOTION_THR :: cell where "HEAT_DEMOTION_THR = 10" lemma HEAT_MAX_cell_safe [simp]: "cell_safe HEAT_MAX" by (simp add: cell_safe_def HEAT_MAX_def word_sle_eq) lemma HEAT_MAX_lt_succ: "HEAT_MAX s HEAT_MAX" by (simp add: HEAT_MAX_def word_sle_eq) (* Word flag bits (match C macros in include/vm.h) *) definition FLAG_FROZEN :: nat where "FLAG_FROZEN = 4" \ \WORD_FROZEN 0x04\ definition FLAG_PINNED :: nat where "FLAG_PINNED = 8" \ \WORD_PINNED 0x08\ (* ========================================================================= Section 2: Heat predicates ======================================================================== *) definition heat_frozen :: "dict_entry \ bool" where "heat_frozen e \ de_flags e AND FLAG_FROZEN = FLAG_FROZEN" definition heat_pinned :: "dict_entry \ bool" where "heat_pinned e \ de_flags e AND FLAG_PINNED = FLAG_PINNED" (* CORRECTED 2026-08-13 (cell = 64 word migration): every comparison on a cell value below uses the Word library's SIGNED order (s, signed.min, signed.max) instead of the default unsigned order (<, \, min, max) that a bare word type gets by default. cell_t is C's *signed* long -- under unsigned order, -1 would compare greater than any positive value, which is simply wrong for FORTH's signed integer semantics. Structural nat/list-length reasoning elsewhere is unaffected. *) definition heat_valid :: "dict_entry \ bool" where "heat_valid e \ 0 \s de_heat e \ de_heat e \s HEAT_MAX" (* Bridging lemma: any de_heat within heat_valid's own range is automatically cell_safe (HEAT_MAX = 10^6 sits far inside the 2^32 safe zone), so lemmas that already assume heat_valid never need to also assume cell_safe separately -- it comes for free. *) lemma heat_valid_imp_cell_safe: assumes "heat_valid e" shows "cell_safe (de_heat e)" using assms by (simp add: heat_valid_def cell_safe_def HEAT_MAX_def word_sle_eq word_sless_alt) (* ========================================================================= Section 3: Heat increment (fired on every word execution) ======================================================================== *) (* Saturating increment: heat grows by 1, capped at HEAT_MAX. *) definition heat_increment :: "dict_entry \ dict_entry" where "heat_increment e = e\de_heat := signed.min (de_heat e + 1) HEAT_MAX\" (* CORRECTED for the cell-as-word migration: added the "cell_safe" side condition. Under a bare 64-bit word, "de_heat e < HEAT_MAX \ de_heat e + 1 \ HEAT_MAX" is only true if the +1 doesn't wrap -- see cell_safe_add_sint in StarForth_Base.thy. Any de_heat within heat_valid's own range is trivially cell_safe (HEAT_MAX is 10^6, far inside the 2^32 safe zone), so this costs nothing in practice. *) lemma heat_increment_correct: assumes "de_heat e s HEAT_MAX" using sint_eq assms(1) by (simp add: word_sle_eq word_sless_alt) thus ?thesis by (simp add: heat_increment_def signed.min_def) qed lemma heat_increment_saturates: assumes "de_heat e = HEAT_MAX" shows "de_heat (heat_increment e) = HEAT_MAX" proof - have "\ HEAT_MAX + 1 \s HEAT_MAX" using HEAT_MAX_lt_succ by (simp add: word_sless_alt word_sle_eq) thus ?thesis by (simp add: heat_increment_def assms signed.min_def) qed (* CORRECTED 2026-08-13: added the missing upper-bound hypothesis. Without it, if de_heat e already exceeds HEAT_MAX, the min-clamp in heat_increment could pull the result back down below de_heat e, breaking monotonicity. *) lemma heat_increment_non_decreasing: assumes "de_heat e \s HEAT_MAX" assumes "cell_safe (de_heat e)" shows "de_heat e \s de_heat (heat_increment e)" proof - have sint_eq: "sint (de_heat e + 1) = sint (de_heat e) + 1" using cell_safe_add_sint[OF assms(2), of 1] by simp have "de_heat e \s de_heat e + 1" using sint_eq by (simp add: word_sle_eq) thus ?thesis using assms(1) by (simp add: heat_increment_def signed.min_def) qed lemma heat_increment_preserves_validity: assumes "heat_valid e" shows "heat_valid (heat_increment e)" proof - have h0: "0 \s de_heat e" and hmax: "de_heat e \s HEAT_MAX" using assms by (simp_all add: heat_valid_def) have safe: "cell_safe (de_heat e)" using assms by (rule heat_valid_imp_cell_safe) have sint_eq: "sint (de_heat e + 1) = sint (de_heat e) + 1" using cell_safe_add_sint[OF safe, of 1] by simp have step_pos: "0 \s de_heat e + 1" using h0 sint_eq by (simp add: word_sle_eq) have le: "de_heat (heat_increment e) \s HEAT_MAX" unfolding heat_increment_def by (simp add: signed.min_def) have ge: "0 \s de_heat (heat_increment e)" unfolding heat_increment_def using step_pos hmax HEAT_MAX_nonneg by (simp add: signed.min_def) from le ge show ?thesis by (simp add: heat_valid_def) qed lemma heat_increment_preserves_flags: "de_flags (heat_increment e) = de_flags e" by (simp add: heat_increment_def) lemma heat_increment_preserves_frozen: "heat_frozen (heat_increment e) = heat_frozen e" by (simp add: heat_frozen_def heat_increment_def) lemma heat_increment_preserves_pinned: "heat_pinned (heat_increment e) = heat_pinned e" by (simp add: heat_pinned_def heat_increment_def) (* ========================================================================= Section 4: Heat decay (fired by heartbeat Loop #3) ======================================================================== *) (* A decay amount in cell units. The C code uses Q48.16 slope × elapsed time, truncated to an integer; we abstract the amount as a parameter. *) definition heat_decay :: "cell \ dict_entry \ dict_entry" where "heat_decay amount e = (if heat_frozen e then e \ \FROZEN: no decay\ else if heat_pinned e then e\de_heat := signed.max 1 (de_heat e - amount)\ \ \PINNED: floor at 1\ else e\de_heat := signed.max 0 (de_heat e - amount)\)" \ \normal: floor at 0\ lemma heat_decay_frozen: assumes "heat_frozen e" shows "heat_decay amount e = e" by (simp add: heat_decay_def assms) (* CORRECTED 2026-08-13: added the missing lower-bound hypothesis. In the PINNED branch, heat_decay floors at max 1 (de_heat e - amount) -- if de_heat e was already below 1 (e.g. 0), the floor raises it, breaking monotonicity. de_heat e \s 1 is the standing invariant a pinned word is supposed to maintain (see heat_decay_pinned_positive's own \s 1 conclusion below), just never stated here as a precondition before. *) lemma heat_decay_monotone: assumes "\ heat_frozen e" assumes "1 \s de_heat e" assumes "0 \s amount" assumes "cell_safe (de_heat e)" assumes "cell_safe amount" shows "de_heat (heat_decay amount e) \s de_heat e" proof - have zero_le: "0 \s de_heat e" using assms(2) by (simp add: word_sle_eq) have sub_sint: "sint (de_heat e - amount) = sint (de_heat e) - sint amount" using cell_safe_sub_sint[OF assms(4) assms(5)] . have amount_nonneg: "0 \ sint amount" using assms(3) by (simp add: word_sle_eq) have sub_le: "de_heat e - amount \s de_heat e" using sub_sint amount_nonneg by (simp add: word_sle_eq) show ?thesis using assms zero_le sub_le by (simp add: heat_decay_def signed.max_def) qed lemma heat_decay_non_negative: assumes "\ heat_frozen e" assumes "\ heat_pinned e" shows "0 \s de_heat (heat_decay amount e)" by (simp add: heat_decay_def assms signed.max_def) lemma heat_decay_pinned_positive: assumes "\ heat_frozen e" assumes "heat_pinned e" shows "1 \s de_heat (heat_decay amount e)" by (simp add: heat_decay_def assms signed.max_def) lemma heat_decay_preserves_validity: assumes "heat_valid e" assumes "\ heat_frozen e" assumes "0 \s amount" assumes "cell_safe amount" shows "heat_valid (heat_decay amount e)" proof - have safe_e: "cell_safe (de_heat e)" using assms(1) by (rule heat_valid_imp_cell_safe) have sub_sint: "sint (de_heat e - amount) = sint (de_heat e) - sint amount" using cell_safe_sub_sint[OF safe_e assms(4)] . have hmax: "de_heat e \s HEAT_MAX" using assms(1) by (simp add: heat_valid_def) have amount_nonneg: "0 \ sint amount" using assms(3) by (simp add: word_sle_eq) have sub_le_max: "de_heat e - amount \s HEAT_MAX" using sub_sint hmax amount_nonneg by (simp add: word_sle_eq) have one_le_max: "1 \s HEAT_MAX" by (simp add: HEAT_MAX_def word_sle_eq) have zero_le_one: "0 \s (1 :: cell)" by (simp add: word_sle_eq) show ?thesis proof (cases "heat_pinned e") case True thus ?thesis using sub_le_max one_le_max zero_le_one by (simp add: heat_valid_def heat_decay_def assms(2) signed.max_def word_sle_eq) next case False thus ?thesis using sub_le_max HEAT_MAX_nonneg by (simp add: heat_valid_def heat_decay_def assms(2) signed.max_def) qed qed lemma heat_decay_preserves_flags: "de_flags (heat_decay amount e) = de_flags e" by (simp add: heat_decay_def) (* ========================================================================= Section 5: Well-formedness of the dictionary heat state ======================================================================== *) (* All dict entries reachable via the dictionary have valid heat. *) definition dict_heat_wf :: "vm_state \ bool" where "dict_heat_wf vm \ (\i e. dictionary vm i = Some e \ heat_valid e)" (* ========================================================================= Section 6: Heat thresholds ======================================================================== *) (* The VM maintains 25th/50th/75th percentile thresholds for bucket search. Well-formedness: thresholds are in ascending order and non-negative. *) definition heat_thresholds_wf :: "vm_state \ bool" where "heat_thresholds_wf vm \ 0 \s heat_threshold_25th vm \ heat_threshold_25th vm \s heat_threshold_50th vm \ heat_threshold_50th vm \s heat_threshold_75th vm \ heat_threshold_75th vm \s HEAT_MAX" (* A pure data-stack word does not change heat thresholds or dictionary heat. *) lemma ds_word_preserves_dict_heat: assumes "dictionary (vm\data_stack := xs\) = dictionary vm" shows "dict_heat_wf (vm\data_stack := xs\) = dict_heat_wf vm" by (simp add: dict_heat_wf_def assms) lemma ds_word_preserves_heat_thresholds: "heat_thresholds_wf (vm\data_stack := xs\) = heat_thresholds_wf vm" by (simp add: heat_thresholds_wf_def) end