"""Print the key numbers of the timing study from data/results.json (written by run_all.py), so that every quoted number can be checked against a rerun. python3 code/summary.py """ from __future__ import annotations import json import os import pandas as pd import config as cfg def main(): r = json.load(open(os.path.join(cfg.DATA, "results.json"))) a1, a2, a3, a4, a5 = (r[k] for k in ("a1_kipping", "a2_hardsteps", "a3_snyder_beattie", "a4_models", "a5_fbio")) P = print P("=" * 78) P("1. KIPPING REPRODUCTION (ours | published)") for case in ("optimistic_zircon_4.1Ga", "conservative_microfossil_3.465Ga"): o, p = a1["kipping2020"][case], a1["kipping2020"]["published"][case] P(f" {case}: limit {o['Z10/Z00_numeric']:.3f} | {p['Z10/Z00']}; fast:slow Jeffreys " f"{o['ZLfast/ZLslow_jeffreys']:.3f} | {p['ZLfast/ZLslow_jeffreys']}; uniform " f"{o['ZLfast/ZLslow_uniform']:.3f} | {p['ZLfast/ZLslow_uniform']}") P(f" rare-intelligence odds: uniform {o['ZIslow/Zens_uniform']:.3f} | {p['ZIslow/Zens_uniform']}; " f"Jeffreys {o['ZIslow/Zens_jeffreys']:.3f} (MC {o['ZIslow/Zens_jeffreys_MC']:.4f} +/- " f"{o['ZIslow/Zens_jeffreys_MC_se']:.4f}) | {p['ZIslow/Zens_jeffreys']}") k = a1["kipping2025"] P(f" 2025: Isua {k['Isua_3.7Ga']:.2f}, zircon {k['zircon_4.1Ga']:.2f}, LUCA {k['LUCA_4.2Ga']:.2f}, " f"LUCA +1.8 Gyr {k['LUCA_future_1.8Gyr']:.2f}, min over T {k['min_over_T']:.2f} | published {k['published']}") P(f" LUCA uncertainty propagated (16/50/84%): {[round(x, 1) for x in k['LUCA_BF_quantiles_16_50_84']]}") P(" plateau ratio B(m), closed form vs brute force:", [(v["m"], round(v["closed_form"], 3), round(v["brute_force"], 3)) for v in a1["bmax_validation"]]) for lab in ("bmax_LUCA_future0.9", "bmax_LUCA_future1.73", "bmax_Dresser_future0.9"): P(f" {lab}: " + ", ".join(f"{kk.split(' later')[0] if 'later' in kk else kk}={vv}" for kk, vv in a1[lab].items())) m = a1["moody"] P(f" Moody repository: {m['n_posterior_estimates']} posterior LUCA ages, {m['LUCA_mean_range_Ga']} Ga; " f"point values T/(2 tLp): median {m['BF_t0_4.404_median']:.1f}, quartiles " f"{[round(x, 1) for x in m['BF_t0_4.404_quartiles']]}, frac > 10: {m['frac_BF_gt_10_t0_4.404']:.2f} for t0 = 4.404; " f"median {m['BF_t0_4.51_median']:.1f}, quartiles {[round(x, 1) for x in m['BF_t0_4.51_quartiles']]}, " f"min-max {m['BF_t0_4.51_min']:.1f}-{m['BF_t0_4.51_max']:.1f}, frac > 10: {m['frac_BF_gt_10_t0_4.51']:.2f} for t0 = 4.51") dv = m["divergent_point_value_t0_4.404"] P(f" (not quoted) largest point value for t0 = 4.404: {dv['point_BF']:.0f}, from '{dv['analysis']}' {dv['clock']} copy " f"{dv['copy']}: LUCA {dv['LUCA_mean_Ga']:.4f} Ga, {dv['gap_to_start_Myr']:.1f} Myr from the start, own 95% interval " f"{dv['LUCA_95pct_Ga']}; {m['n_variants_whose_95pct_interval_contains_4.404']} of {m['n_posterior_estimates']} variants " f"have a 95% interval that contains 4.404 Ga") for kk, v in m["calibration_only_prior_LUCA"].items(): P(f" calibration-only prior on LUCA, {kk} (n = {v['n']}): {v['mean_Ga']:.2f} ({v['q025_Ga']:.2f}-{v['q975_Ga']:.2f}) Ga") P(" root, paper-main runs:", [(x["clock"], x["stage"], round(x["mean_Ga"], 3), round(x["q025_Ga"], 3), round(x["q975_Ga"], 3)) for x in m["root_paper_main"]], f"hard maximum {m['root_hard_maximum_Ga']} Ga") P(" paper-main LUCA rows:", [(x["clock"], x["stage"], round(x["LUCA_mean_Ga"], 3), round(x["LUCA_q025_Ga"], 3), round(x["LUCA_q975_Ga"], 3)) for x in m["paper_main"]]) P(" LUCA copies without cross-bracing (gap, Gyr):", [round(x["gap_Gyr"], 2) for x in m["non_cross_braced_copy_gaps"]]) for key in ("paper main (ILN+GBM) | t0 ~ U(4.404, 4.50) | future mix", "paper main (ILN+GBM) | t0 = 4.51 fixed | future 0.9 fixed", "all 38 posterior estimates | t0 ~ U(4.404, 4.50) | future mix", "all 38 posterior estimates | t0 = 4.51 fixed | future mix"): v = m["marginalised"][key] P(f" marginalised B(m=1..5), {key}: " + ", ".join(f"{v[f'B_marg_m{i}']:.2f}" for i in range(1, 6)) + f" [m=1 pointwise 16-84%: {v['B_pointwise_16_84_m1'][0]:.1f}-{v['B_pointwise_16_84_m1'][1]:.1f}; " f"draws with LUCA older than start excluded: {v['frac_draws_LUCA_older_than_t0']:.3f}]") P(" plateau ratio when later steps are NOT all hard (B(m) is not a ceiling):") for lab, v in a1["later_process_sensitivity"].items(): if lab in ("validation", "curve_baseline"): continue P(f" {lab}: all-hard m=1 {v['all_hard_m1']:.1f}, m=5 {v['all_hard_m5']:.2f}; one later step, exact arrival time: " f"max {v['one_later_step_exact_time_max']:.1f} at {v['rate_at_max_per_Gyr']:.2f}/Gyr; Kipping interval form, fast " f"intelligence: {v['kipping_interval_limit_fast_intelligence_T_over_tLp']:.1f}; pacing deadline today " f"{v['pacing_deadline_today_tobs_over_tLp']:.1f}, GOE {v['pacing_deadline_GOE']:.1f}; one hard + one at 0.1/1/10 per Gyr: " f"{v['two_later_steps_one_hard_one_rate_0.1']:.1f}/{v['two_later_steps_one_hard_one_rate_1']:.1f}/" f"{v['two_later_steps_one_hard_one_rate_10']:.1f}; two free later steps max {v['two_later_steps_max']:.1f}") P(" validation (rate, exact closed/numeric/general, interval closed/numeric):", [(x["rate"], round(x["exact_closed"], 3), round(x["exact_numeric"], 3), round(x["exact_general_routine"], 3), round(x["interval_closed"], 3), round(x["interval_numeric"], 3)) for x in a1["later_process_sensitivity"]["validation"]]) for case in ("optimistic_zircon_4.1Ga", "conservative_microfossil_3.465Ga"): P(f" Jeffreys odds if the ensemble integral omits f_I < 0.0032 ({case}): " f"{a1['kipping2020'][case]['ZIslow/Zens_jeffreys_if_ensemble_omits_fI_below_0.0032']:.3f} " f"| published {a1['kipping2020']['published'][case]['ZIslow/Zens_jeffreys']}") ps = pd.DataFrame(a1["prior_sensitivity"]) ps = ps[ps.intelligence_rate.str.startswith("lambda_I marg")] P(" prior sensitivity, P(rate > 1/Gyr) prior -> posterior:") for x in ps.itertuples(): P(f" {x.life_evidence[:12]:12s} {x.prior:55s} {x.prior_P_fast:.3f} -> {x.post_P_fast:.3f}") P("=" * 78) P("2. HARD STEPS") for case, v in a2["n_posteriors"].items(): a, b, c = (v[f"{s} | flat prior n=1..20"] for s in ("last step only", "last step, 1.5 Gyr delay", "first (abiogenesis hard) + last")) P(f" {case}: MLE n {v['n_mle_last_step']:.2f}; last-step mode {a['mode']}, 90% {a['lo90']}-{a['hi90']}, " f"like max/min {a['max_over_min_like_n_le_10']:.2f}; with delay mode {b['mode']}; first+last median {c['median']}") g = pd.DataFrame(a2["gof"]) pc = [c for c in g.columns if c.startswith("p_")] P(f" goodness of fit: {len(g) * len(pc)} p-values, smallest {g[pc].min().min():.4f}") for x in g.itertuples(): P(f" n={x.n} future {x.future[:4]:4s} first-early {x.p_first_step_this_early:.3f} greenwood {x.p_two_sided_greenwood:.3f} " f"ks {x.p_two_sided_ks:.3f} too-even {x.p_too_even_greenwood:.3f}") P(" power:", {kk: {a: round(b, 2) for a, b in vv.items()} for kk, vv in a2["gof_power_n5_alpha0.05"].items()}) P("=" * 78) P("3. SNYDER-BEATTIE ET AL. TABLE 2 (published | ours with e^-k | ours with 10^-k)") for x in a3["table2"]: P(f" {x['transitions_Gya']:22s} {x['pub_1']:.4f} {x['pub_2']:.3f} {x['pub_3']:.2f} | " f"{x['ours_epow_1']:.4f} {x['ours_epow_2']:.3f} {x['ours_epow_3']:.2f} | " f"{x['ours_10pow_1']:.4f} {x['ours_10pow_2']:.3f} {x['ours_10pow_3']:.3f}") s = a3["table2_summary"] P(f" rms log10 deviation: e^-k {s['rms_log10_dev_thresholds_e^-k']:.3f}, 10^-k {s['rms_log10_dev_thresholds_as_labelled_10^-k']:.3f}; " f"smallest two-step probability the stated prior allows: {s['min_possible_P_n2_stated_prior']:.2e}") P(" Table 3 published:", a3["table3_published_log10_bound"]) P(" Table 3 ours :", a3["table3_ours_log10_bound"]) P(" Table 3, ours minus published (dex):", a3["table3_deviation_dex"]) for kk, v in a3["verbal_statements"].items(): P(f" {kk}: posterior weight below 1% {v['post_weight_below_1pct'][0]:.3f}-{v['post_weight_below_1pct'][1]:.3f}; " f"below 1e-12 {v['post_weight_below_1e-12'][0]:.3f}-{v['post_weight_below_1e-12'][1]:.3f}; " f"below 1e-24 {v['post_weight_below_1e-24'][0]:.3f}-{v['post_weight_below_1e-24'][1]:.3f}") lb = pd.DataFrame(a3["lower_bound_sensitivity"]) P(" P(expected abiogenesis time > window), prior -> posterior, by LOWER prior bound on the mean time (Ga):") for nn, g in lb.groupby("n"): P(f" {nn} steps: " + "; ".join(f"1e{int(x.log10_beta_min)}: {x.prior_P_beta1_gt_T:.2f} -> {x.post_P_beta1_gt_T:.2f}" for x in g.itertuples())) u = pd.DataFrame(a3["updated"]) u = u[(u.future == "mix") & (u.log10_beta_max == 10.0)] for x in u.itertuples(): later = [getattr(x, f"post_P_beta{i}_gt_T") for i in range(2, x.n + 1)] P(f" updated dates, {x.model[:8]} {x.abiogenesis}: P(abiogenesis mean wait > T) {x.post_P_beta1_gt_T:.3f}; " f"later steps {[round(v, 2) for v in later]}; any {x.post_P_any_beta_gt_T:.3f}") P("=" * 78) P("4. HARD STEPS vs PACING (Bayes factors)") for x in a4["given_gates"]: P(f" {x['case']:36s} K={x['K']}: GH:H {x['BF_GH_over_H']:.2f}; P:H {x['BF_P_over_H | mu~LU[0.01,3]']:.2f}; " f"P:GH {x['BF_P_over_GH | mu~LU[0.01,3]']:.2f} (other mu priors {x['BF_P_over_GH | mu~LU[0.001,10]']:.2f}, " f"{x['BF_P_over_GH | mu~LU[0.03,1]']:.2f}, {x['BF_P_over_GH | independent mu_j~LU[0.01,3]']:.2f}); " f"gating ok in {x['frac_date_draws_with_every_transition_after_its_gate']:.2f} of draws") P(" gate-definition sensitivity (central dates; eukaryotes, animals, humans):") for x in a4["gate_definition_sensitivity"]: P(f" {x['case']:55s} +{x['future_Gyr']}: GH:H {x['BF_GH_over_H']:.2f}; P:H {x['BF_P_over_H']:.2f}; " f"P:GH {x['BF_P_over_GH'] if x['BF_P_over_GH'] is None else round(x['BF_P_over_GH'], 2)}") for F, v in a4["lag_test_under_GH"].items(): P(f" lag test under GH, {F}: p(3 lags this short) = {v['p_sum_of_3_lags_this_small']:.4f}") for F, v in a4["biospheres_needed"].items(): P(f" biospheres for median BF > 100, {F}: truth P {v['P']['N_for_median_decisive']}, truth GH {v['GH']['N_for_median_decisive']}") c = pd.DataFrame(a4["carter_coincidence"]) cols = [x for x in c.columns if x.startswith("BF_")] P(f" Carter coincidence, BF hard steps : pacing from t_obs/T: uniform gate prior {c[cols[0]].min():.2f}-{c[cols[0]].max():.2f}; " f"log-uniform gate prior {c[cols[1:]].min().min():.2f}-{c[cols[1:]].max().max():.2f}") P("=" * 78) P("5. f_bio: log10 quantiles (5/50/95) prior -> posterior, information gained") for x in a5["families"]: P(f" {x['family'][:44]:44s} {x['prior_q05']:7.2f} {x['prior_q50']:7.2f} {x['prior_q95']:6.2f} -> " f"{x['post_q05']:7.2f} {x['post_q50']:7.2f} {x['post_q95']:6.2f} {x['KL_post_vs_prior_bits']:.2f} bits") P(" same families WITHOUT conditioning on success (self-indication): log10 f_bio 5/50/95, ESS; KL at 20/40/80 bins") for x in a5["families"]: P(f" {x['family'][:44]:44s} {x['sia_q05']:7.2f} {x['sia_q50']:7.2f} {x['sia_q95']:6.2f} ESS {x['sia_ESS']:.0f} " f"P(f<0.1) {x['sia_P_log10f_lt_-1']:.3f} P(f<0.01) {x['sia_P_log10f_lt_-2']:.3f} P(f<1e-6) {x['sia_P_log10f_lt_-6']:.1e} " f"KL {x['KL_bits_20bins']:.2f}/{x['KL_post_vs_prior_bits']:.2f}/{x['KL_bits_80bins']:.2f} bits " f"(conditioned ESS {x['ESS']:.0f})") P(" unconditioned posterior vs prior bounds:", [(x["family"], round(x["sia_q50"], 2)) for x in a5["sia_prior_bound_sensitivity"]]) P(" grid check, B2 at central dates:", {k: [round(v, 2) for v in vv] for k, vv in a5["sia_grid_check_B2_central_dates"].items()}) P(" P(f_life > 0.5) prior -> posterior:", [(x["family"][:2], round(x["prior_P_flife_gt_0.5"], 2), round(x["post_P_flife_gt_0.5"], 2)) for x in a5["families"] if "post_P_flife_gt_0.5" in x and x["post_P_flife_gt_0.5"] == x["post_P_flife_gt_0.5"]]) P(" profile likelihood of f_bio (two-step model):") for x in a5["profile_likelihood"]: P(f" future {x['future']}: f_bio = {x['f_bio']:<8g} {x['relative_profile_likelihood']:.3f} ({x['path']})") if __name__ == "__main__": main()