algo/applications/collatz/collatz_k_scripts/collatz_compute_survival_rate.py
Nicolas Cantu 14ed1de36b Pipeline Collatz aligné sur commandes.md et reprise après interruption
**Motivations:**
- Implémenter le workflow complet de démonstration Collatz (commandes.md)
- Permettre la reprise après interruption au palier D20

**Evolutions:**
- Scripts 01-12 et run-full-workflow alignés sur commandes.md sections 1-10
- collatz_recover_noyau.py : recréation de noyau_post_D20 à partir du CSV candidats
- Option --resume-from D20 dans collatz_k_pipeline pour reprendre sans recalculer D18-D19-F15
- Détection automatique : si candidats_D20 existe sans noyau_post_D20, récupération puis poursuite
- Filtres --cible=critique et --modulo dans collatz_fusion_pipeline
- ROOT par défaut = collatz_k_scripts (plus data/source vide)

**Pages affectées:**
- .gitignore (__pycache__, out/)
- applications/collatz/collatz_k_scripts/*.py
- applications/collatz/scripts/*.sh
- applications/collatz/scripts/README.md
2026-03-02 02:49:23 +01:00

66 lines
2.1 KiB
Python

# -*- coding: utf-8 -*-
"""
collatz_compute_survival_rate.py
Compute survival rate (coefficient of contraction) for a noyau.
Outputs |B_m| and q = |B_m| / (2 * |R_{m-1}|) when previous noyau is available.
Usage: python collatz_compute_survival_rate.py --noyau PATH [--previous PATH]
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
def load_noyau(path: str) -> tuple[list[int], int]:
"""Load noyau from JSON, return (residues, palier)."""
data = json.loads(Path(path).read_text(encoding="utf-8"))
if isinstance(data, list):
residues = [int(x) for x in data]
palier = max(n.bit_length() for n in residues) if residues else 0
return residues, palier
if isinstance(data, dict):
for key in ("noyau", "residues", "uncovered"):
if key in data and isinstance(data[key], list):
residues = [int(x) for x in data[key]]
palier = data.get("palier", max(n.bit_length() for n in residues) if residues else 0)
return residues, palier
raise ValueError(f"Cannot load noyau from {path}")
def main() -> None:
ap = argparse.ArgumentParser(description="Compute survival rate of noyau")
ap.add_argument("--noyau", required=True, help="Path to noyau JSON")
ap.add_argument("--previous", help="Path to previous noyau (for q = |B_m| / 2|R_{m-1}|)")
args = ap.parse_args()
residues, palier = load_noyau(args.noyau)
size = len(residues)
lines = [
f"# Survival rate: {Path(args.noyau).name}",
"",
f"- |B_{{m}}| = {size}",
f"- palier m ≈ {palier}",
"",
]
if args.previous and Path(args.previous).exists():
prev_residues, prev_palier = load_noyau(args.previous)
prev_size = len(prev_residues)
denom = 2 * prev_size
q = size / denom if denom else 0
lines.extend([
f"- |R_{{m-1}}| = {prev_size}",
f"- q = |B_m| / (2|R_{{m-1}}|) = {q:.6f}",
"",
])
print("\n".join(lines))
if __name__ == "__main__":
main()