Skip to content

Commit 86bb860

Browse files
committed
fix: Add chi_g peak fallback for Re_crit + smoke test infrastructure
- thermal_pump.py: If Cv peak hasn't fired by chi_g peak, derive Re_crit from chi_g peak conditions (also a valid phase transition marker) - sprint_c_tower_validation.py: Add --smoke_test, --verbose, --early_stop_on_grok - Smoke test uses --quick mode (p=17) for faster verification - Verbose mode shows Cv, Re_SGC, Re_crit in training log Smoke test verified: Cv peak detected: True T_peak = 1.0000 Re_crit = 0.0100 (SELF-DERIVED) [GO] Thermodynamic control loop verified. Ready for full run.
1 parent 30c1bf4 commit 86bb860

File tree

2 files changed

+68
-7
lines changed

2 files changed

+68
-7
lines changed

perihelion/core/thermal_pump.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ def detect_chi_g_peak(self) -> bool:
201201
ZERO-PARAMETER: No threshold comparison.
202202
Peak is when d(chi_g)/dt changes from positive to negative.
203203
This is a detectable event, not a threshold.
204+
205+
FALLBACK: If Cv peak hasn't been detected by the time chi_g peaks,
206+
use the chi_g peak conditions to derive Re_crit. This ensures the
207+
Fermi quench has a valid critical Reynolds number even when
208+
energy variance is too low for Cv peak detection.
204209
"""
205210
if len(self.chi_g_history) < 10:
206211
return False
@@ -220,6 +225,13 @@ def detect_chi_g_peak(self) -> bool:
220225
self.chi_g_peak_detected = True
221226
self.chi_g_peak_epoch = self.epoch
222227
self.chi_g_peak_value = max(recent)
228+
229+
# FALLBACK: If Cv peak hasn't fired, derive Re_crit from chi_g peak
230+
# chi_g peak is also a valid phase transition marker
231+
if not self.Cv_peak_detected and self.grad_epsilon_norm > 1e-10:
232+
self.Re_crit = (self.kappa * self.temperature * self.lambda_pump) / self.grad_epsilon_norm
233+
print(f"[ThermalPump] chi_g PEAK -> Re_crit = {self.Re_crit:.4f} (FALLBACK)")
234+
223235
return True
224236

225237
return False

perihelion/experiments/sprint_c_tower_validation.py

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -648,10 +648,23 @@ def train_task(self, task) -> TaskResult:
648648
if step % self.log_interval == 0:
649649
train_acc, test_acc = self.compute_accuracy(task_name)
650650
sigma_q = self.controller.thermal.get_fermi_quench_factor()
651-
print(f"Step {step:5d} | Train: {train_acc:.3f} | Test: {test_acc:.3f} | "
652-
f"eps: {metrics.epsilon:.4f} | R: {metrics.ridge_ratio:.2f} | "
653-
f"T: {self.controller.thermal.temperature:.2f} | "
654-
f"sigma_q: {sigma_q:.3f}")
651+
thermal = self.controller.thermal
652+
653+
# Basic metrics
654+
log_line = (f"Step {step:5d} | Train: {train_acc:.3f} | Test: {test_acc:.3f} | "
655+
f"eps: {metrics.epsilon:.4f} | R: {metrics.ridge_ratio:.2f} | "
656+
f"T: {thermal.temperature:.2f} | sigma_q: {sigma_q:.3f}")
657+
658+
# Verbose: add Cv and Re_SGC tracking
659+
if getattr(self, 'verbose', False):
660+
Cv = 0.0
661+
if len(thermal.Cv_history) > 0:
662+
Cv = thermal.Cv_history[-1][1]
663+
log_line += f" | Cv: {Cv:.3f} | Re: {thermal.Re_SGC:.3f}"
664+
if thermal.Cv_peak_detected:
665+
log_line += f" | Re_crit: {thermal.Re_crit:.3f}"
666+
667+
print(log_line)
655668

656669
# Phase 3: Preservation monitoring
657670
if step % self.preservation_check_interval == 0 and self.tower_result.tasks:
@@ -773,12 +786,28 @@ def main():
773786
parser.add_argument('--output', type=str, default='sprint_c_tower_result.json')
774787
parser.add_argument('--quick', action='store_true',
775788
help='Quick test with reduced parameters')
789+
parser.add_argument('--verbose', action='store_true',
790+
help='Verbose logging including Cv tracking')
791+
parser.add_argument('--smoke_test', action='store_true',
792+
help='Run 500-step smoke test to verify Cv peak detection')
793+
parser.add_argument('--early_stop_on_grok', action='store_true',
794+
help='Stop task immediately after grokking (for smoke test)')
776795

777796
args = parser.parse_args()
778797

798+
# Smoke test mode
799+
if args.smoke_test:
800+
args.max_steps = 500
801+
args.verbose = True
802+
args.early_stop_on_grok = True
803+
args.quick = True # Use smaller prime for smoke test
804+
print("\n" + "="*60)
805+
print("SMOKE TEST MODE: 500 steps, watching for Cv peak")
806+
print("="*60)
807+
779808
if args.quick:
780-
args.max_steps = 2000
781-
prime = 17
809+
args.max_steps = min(args.max_steps, 2000)
810+
prime = 17 # Smaller prime for quick/smoke tests
782811
else:
783812
prime = 97
784813

@@ -796,13 +825,33 @@ def main():
796825
hidden_dim=args.hidden_dim,
797826
device=args.device,
798827
max_steps_per_task=args.max_steps,
799-
log_interval=100,
828+
log_interval=50 if args.verbose else 100,
800829
preservation_check_interval=500
801830
)
802831

832+
# Pass verbosity setting
833+
conductor.verbose = getattr(args, 'verbose', False)
834+
conductor.early_stop_on_grok = getattr(args, 'early_stop_on_grok', False)
835+
conductor.smoke_test = getattr(args, 'smoke_test', False)
836+
803837
result = conductor.run()
804838
conductor.save_results(args.output)
805839

840+
# Smoke test summary
841+
if args.smoke_test:
842+
print("\n" + "="*60)
843+
print("SMOKE TEST SUMMARY")
844+
print("="*60)
845+
cv_detected = conductor.controller.thermal.Cv_peak_detected
846+
print(f"Cv peak detected: {cv_detected}")
847+
if cv_detected:
848+
print(f" T_peak = {conductor.controller.thermal.Cv_peak_temperature:.4f}")
849+
print(f" Re_crit = {conductor.controller.thermal.Re_crit:.4f} (SELF-DERIVED)")
850+
print("\n[GO] Thermodynamic control loop verified. Ready for full run.")
851+
else:
852+
print("\n[WAIT] Cv peak not detected in 500 steps.")
853+
print(" Check energy tracking in thermal_pump.update_energy()")
854+
806855
return result
807856

808857

0 commit comments

Comments
 (0)