175 lines
5.9 KiB
Python
175 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
||
"""Quick analysis of window_scaling heartbeat data"""
|
||
|
||
# StarForth — Steady-State Virtual Machine Runtime
|
||
#
|
||
# Copyright (c) 2023–2025 Robert A. James
|
||
# All rights reserved.
|
||
#
|
||
# This file is part of the StarForth project.
|
||
#
|
||
# Licensed under the StarForth License, Version 1.0 (the "License");
|
||
# you may not use this file except in compliance with the License.
|
||
#
|
||
# You may obtain a copy of the License at:
|
||
# https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
#
|
||
# This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
# express or implied, including but not limited to the warranties of
|
||
# merchantability, fitness for a particular purpose, and noninfringement.
|
||
#
|
||
# See the License for the specific language governing permissions and
|
||
# limitations under the License.
|
||
#
|
||
# StarForth — Steady-State Virtual Machine Runtime
|
||
# Copyright (c) 2023–2025 Robert A. James
|
||
# All rights reserved.
|
||
#
|
||
# This file is part of the StarForth project.
|
||
#
|
||
# Licensed under the StarForth License, Version 1.0 (the "License");
|
||
# you may not use this file except in compliance with the License.
|
||
#
|
||
# You may obtain a copy of the License at:
|
||
# https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
#
|
||
# This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
# express or implied, including but not limited to the warranties of
|
||
# merchantability, fitness for a particular purpose, and noninfringement.
|
||
#
|
||
# See the License for the specific language governing permissions and
|
||
# limitations under the License.
|
||
#
|
||
|
||
import os
|
||
import glob
|
||
import csv
|
||
from collections import defaultdict
|
||
import statistics
|
||
|
||
# Find all heartbeat CSV files
|
||
hb_dir = "results_run_one/raw/hb"
|
||
files = sorted(glob.glob(os.path.join(hb_dir, "run-*.csv")))
|
||
|
||
print(f"Found {len(files)} heartbeat files\n")
|
||
|
||
# Collect per-run summary
|
||
runs = []
|
||
window_sizes = defaultdict(list)
|
||
k_values = defaultdict(list)
|
||
|
||
for filepath in files:
|
||
run_id = os.path.basename(filepath).replace("run-", "").replace(".csv", "")
|
||
|
||
with open(filepath, 'r') as f:
|
||
reader = csv.DictReader(f)
|
||
rows = list(reader)
|
||
|
||
if not rows:
|
||
continue
|
||
|
||
# Get W_max from first row
|
||
w_max = int(rows[0]['window_width'])
|
||
|
||
# Collect tick intervals for frequency calculation
|
||
intervals = [float(row['tick_interval_ns']) for row in rows[1:] if float(row['tick_interval_ns']) < 1e9]
|
||
|
||
if not intervals:
|
||
continue
|
||
|
||
# Calculate frequency (1/mean_interval in Hz)
|
||
mean_interval_ns = statistics.mean(intervals)
|
||
freq_hz = 1e9 / mean_interval_ns if mean_interval_ns > 0 else 0
|
||
|
||
# Get K statistics
|
||
k_vals = [float(row['K_approx']) for row in rows if float(row['K_approx']) > 0]
|
||
mean_k = statistics.mean(k_vals) if k_vals else 0
|
||
|
||
# Store
|
||
window_sizes[w_max].append(freq_hz)
|
||
k_values[w_max].append(mean_k)
|
||
|
||
runs.append({
|
||
'run_id': run_id,
|
||
'w_max': w_max,
|
||
'freq_hz': freq_hz,
|
||
'mean_k': mean_k,
|
||
'ticks': len(rows)
|
||
})
|
||
|
||
# Summary by window size
|
||
print("="*70)
|
||
print("FREQUENCY (ω₀) BY WINDOW SIZE")
|
||
print("="*70)
|
||
print(f"{'W_max':<10} {'Runs':<6} {'Mean ω₀ (Hz)':<15} {'Std Dev':<10} {'CV (%)':<8}")
|
||
print("-"*70)
|
||
|
||
for w in sorted(window_sizes.keys()):
|
||
freqs = window_sizes[w]
|
||
if len(freqs) > 1:
|
||
mean_f = statistics.mean(freqs)
|
||
std_f = statistics.stdev(freqs)
|
||
cv = (std_f / mean_f * 100) if mean_f > 0 else 0
|
||
print(f"{w:<10} {len(freqs):<6} {mean_f:<15.3f} {std_f:<10.3f} {cv:<8.2f}")
|
||
|
||
print("\n")
|
||
print("="*70)
|
||
print("JAMES LAW K STATISTIC BY WINDOW SIZE")
|
||
print("="*70)
|
||
print(f"{'W_max':<10} {'Runs':<6} {'Mean K':<12} {'Std Dev':<10} {'|K-1|':<8}")
|
||
print("-"*70)
|
||
|
||
for w in sorted(k_values.keys()):
|
||
k_vals = k_values[w]
|
||
if len(k_vals) > 1:
|
||
mean_k = statistics.mean(k_vals)
|
||
std_k = statistics.stdev(k_vals)
|
||
dev_from_1 = abs(mean_k - 1.0)
|
||
print(f"{w:<10} {len(k_vals):<6} {mean_k:<12.6f} {std_k:<10.6f} {dev_from_1:<8.6f}")
|
||
|
||
print("\n")
|
||
print("="*70)
|
||
print("KEY FINDINGS")
|
||
print("="*70)
|
||
|
||
# Calculate overall stats
|
||
all_freqs = [r['freq_hz'] for r in runs]
|
||
all_k = [r['mean_k'] for r in runs]
|
||
|
||
if all_freqs:
|
||
overall_freq = statistics.mean(all_freqs)
|
||
overall_freq_std = statistics.stdev(all_freqs) if len(all_freqs) > 1 else 0
|
||
overall_freq_cv = (overall_freq_std / overall_freq * 100) if overall_freq > 0 else 0
|
||
|
||
print(f"1. Overall ω₀: {overall_freq:.3f} ± {overall_freq_std:.3f} Hz (CV={overall_freq_cv:.2f}%)")
|
||
|
||
if all_k:
|
||
overall_k = statistics.mean(all_k)
|
||
overall_k_std = statistics.stdev(all_k) if len(all_k) > 1 else 0
|
||
|
||
print(f"2. Overall K: {overall_k:.6f} ± {overall_k_std:.6f} (Deviation from 1.0: {abs(overall_k-1.0):.6f})")
|
||
|
||
# Frequency stability across window sizes
|
||
freq_means = [statistics.mean(window_sizes[w]) for w in sorted(window_sizes.keys()) if len(window_sizes[w]) > 1]
|
||
if len(freq_means) > 1:
|
||
freq_cv_across_windows = (statistics.stdev(freq_means) / statistics.mean(freq_means) * 100)
|
||
print(f"3. Frequency CV across window sizes: {freq_cv_across_windows:.2f}%")
|
||
if freq_cv_across_windows < 5:
|
||
print(" ✓ HYPOTHESIS SUPPORTED: ω₀ is invariant across W_max")
|
||
else:
|
||
print(" ✗ HYPOTHESIS REJECTED: ω₀ varies significantly with W_max")
|
||
|
||
# K stability
|
||
k_means = [statistics.mean(k_values[w]) for w in sorted(k_values.keys()) if len(k_values[w]) > 1]
|
||
if k_means:
|
||
k_deviation = statistics.mean([abs(k-1.0) for k in k_means])
|
||
print(f"4. Mean K deviation from 1.0: {k_deviation:.6f}")
|
||
if k_deviation < 0.1:
|
||
print(" ✓ JAMES LAW VALIDATED: K ≈ 1.0 across conditions")
|
||
else:
|
||
print(" ✗ JAMES LAW NOT VALIDATED: K deviates significantly from 1.0")
|
||
|
||
print("\n")
|
||
print(f"Total runs analyzed: {len(runs)}")
|
||
print(f"Window sizes tested: {sorted(window_sizes.keys())}")
|
||
print("="*70) |