ICT-Synthèse — un seul appareil de mesure, cinq substrats, une question falsifiable

← ICT-Series | ↑ IIT | Epic #4588, capstone — voir #4946

La série ICT (Integrated Causal Trajectories) a promené un même regard — la structure causale d’un système le long de sa trajectoire — sur des substrats de plus en plus riches : le tri auto-organisé (strate 1), les paysages d’attracteurs et la morphogenèse réaction-diffusion (strate 2), les agents et les stratégies (strate 3). À chaque étape, une capacité (robustesse, agence, émergence, anticipation, stabilité stratégique) n’a été créditée qu’après avoir battu un contrôle dégénéré — jamais déclarée. Et le même mot est revenu, notebook après notebook : régime-dépendance.

Ce notebook de synthèse ne rejoue pas la série. Il replie son fil en une seule question falsifiable :

Existe-t-il un scalaire d’intégration qui transfère d’un substrat à l’autre — qui classe le tri, le paysage bistable et le réplicateur stratégique dans le même ordre — ou bien le seul invariant de la série est-il la méthode elle-même (créditer une émergence uniquement contre son propre contrôle dégénéré) ?

Le fil unificateur est un appareil de mesure unique, appliqué à l’identique aux trois substrats : depuis la trajectoire propre de chaque substrat, on estime une matrice de transition état-à-état (le pont tri → TPM d’ICT-6, généralisé) ; puis on lui applique la même batterie d’émergence causale (ICT-5/ICT-6, cadre Causal Emergence 2.0 de Jansma & Hoel). C’est le seul instrument de toute la série branché sans modification sur des substrats radicalement différents.

Statut épistémique — Établi (Gate 4) : Φ/F/K ordonnent les substrats différemment — Φ-F covarient (τ de Kendall = +1.00), K diverge ; l’invariant est dans la méthode, pas dans le nombre. Établi (Gate 5) : la réversibilisation (σ, distance à P_rev) discrimine orthogonalement aux jambes Φ/F/K — l’asymétrie temporelle est la jambe manquante du triplet. Portée et détail dans la matrice de dissociations.

1. Le fil de la série — tableau récapitulatif

Le cœur de ce capstone : une ligne par notebook, la question centrale, le verdict réellement committé (pas l’intuition de départ), et les chiffres des gates qui le soutiennent. On y lit deux constantes de la série — (a) une capacité n’est créditée qu’au contraste d’un contrôle dégénéré, et (b) le verdict est, de plus en plus, régime-dépendant (ICT-10 → ICT-13).

ICT-N Question centrale Verdict réel committé Chiffres-clés des gates
ICT-0 De l’état (IIT) à la trajectoire : quel cadre ? Cadre méthodologique : mesurer ce que le modèle fait vraiment, pas ce qu’on espérait ; deux strates ; contrôle dégénéré obligatoire doc de cadrage (2 articles fondateurs : Levin ; Jansma & Hoel)
ICT-1 \(\Phi\) a-t-il un paysage, une trajectoire, une robustesse ? OUI aux trois : \(\Phi\) pulse le long de l’attracteur et se répare (vrai PyPhi) AND/OR pic \(\Phi\)=2.31 vs 0.19 (amplitude 2.12) ; XOR plat 1.875 ; attracteur cycle longueur 3
ICT-2 Le tri est-il une morphogenèse à compétences ? Robustesse + délai de gratification + auto-réparation OUI ; agrégation « kin » NON (impasses) → renvoi ICT-4 tri homogène 315 pas / 68 swaps ; chimère sortedness 0.784, 4 impasses résiduelles
ICT-3 Ces compétences se quantifient-elles ? OUI : dégradation gracieuse, réparation 24/24, délai ~90 épisodes @50 % lésion passif 0.59–0.62 vs obstacle 0.53 ; délai bubble 91.4 / insertion 89.8
ICT-4 Quel enrichissement minimal fait émerger l’agrégation « kin » ? Réparation bidirectionnelle guérit l’impasse mais n’agrège pas ; l’agrégation émerge seulement dans les degrés de liberté laissés par le tri copies=1 (liberté nulle) → agrégation kin ≈ 0 ; décolle dès copies ≥ 2 ; 40/40 → 0/40 non triés
ICT-5 L’émergence causale (\(\Phi_\text{macro} > \Phi_\text{micro}\)) est-elle réelle et systématique ? Discriminante, pas systématique : absente sur des systèmes déjà micro-intégrés Hoel 0.114 → 0.597 (+0.483) ; XOR −1.375 ; basic_network −1.792
ICT-6 Peut-on porter l’émergence au tri au-delà de PyPhi (CE 2.0) ? OUI ; le macro intuitif (niveau de tri) échoue, seul l’apportionnement glouton trouve l’échelle micro EI 3.29 bits ; macro-tri gain −0.034 (NON) ; glouton effectiveness 0.717 → 0.826 (+0.109)
ICT-7 Le tri auto-organisé est-il scale-free ? NON : échelle caractéristique (la taille \(n\), révélée par effondrement d’échelle) ; le scale-free est rare et réglé branchement critique \(\mu\)=1 : \(\alpha\)=1.58, KS=0.017, \(p\)=0.625 (\(\tau\)=3/2) ; tri rejeté \(p\)-GoF=0.0
ICT-8 Les early-warning signals montent-ils avant la bascule ? OUI (variance ↑, AR1 ↑) ; une mesure naïve fabrique ou masque le signal \(c_\text{fold}\)≈2.604 ; stationnaire \(\tau\)=+1.00 ; AR1 brute 0.9987 (saturée) vs amincie 0.939
ICT-9 Quand un système répare-t-il sa propre forme (agence) ? Agence = gain de réparation > 0 (réaction-diffusion vs diffusion pure) ; mesure naïve = signal fantôme gain=1.22 ; cosinus spectral RD 0.631 < diffusion 0.735 (fantôme) ; sweep 9/9 positif (moy 1.26)
ICT-10 La grammaire des catastrophes est-elle mesurable ? \(\hat p\) anticipe-t-il ? OUI en régime non dégénéré (\(a<0\)) ; hors régime = fantôme. \(\hat p\) : avantage régime-dépendant comptage [1,3], 2 transitions ; lacet aire signée +1.633 ; \(\hat p\) horizon-4 0.055 vs persistance 0.101 ; \(a\)=+0.5 → 0 saut
ICT-11 À quelle échelle l’agence est-elle la plus lisible ? Résultat négatif robuste : pas de granularité privilégiée 2 mesures se contredisent ; effectiveness↔︎repair_gain \(r\)=+0.50 mais ↔︎basin_return \(r\)=−0.14 ; effectiveness sans sommet (0.71/0.50/0.79/0.63)
ICT-12 Dans quels environnements le modèle interne \(\hat p\) paie-t-il son coût ? Régime-dépendant : \(\hat p\) paie où la source échappe au réactif ET reste prévisible balistique rapide capture \(\hat p\) 0.072 vs réactif 0.018 ; anticipation \(\hat p\) 2.44 vs persistance 14.95 (balist.) mais perd en bruité (0.62 vs 0.23)
ICT-13 Une stratégie est-elle une forme stable ? Stable seulement dans un paysage donné ; TFT gagne sans bruit mais s’effondre ; correction honnête : grim chute le moins sans bruit TFT/grim 2.635, AllD 2.313 ; \(\delta^*\)=0.5 (num. 0.55) ; chute sous bruit TFT +0.399 vs grim +0.287 ; invasion TFT/grim 0.02, pavlov/allc jamais (1.0)

Ce que le tableau donne à voir. La série ne consacre presque rien sans réserve : à chaque ligne, un « OUI » s’accompagne d’un « mais » chiffré, et le dernier tiers (ICT-10 → 13) répète le même mot — régime-dépendant. La question du capstone est de savoir si cette répétition cache un scalaire commun transférable, ou si elle est le résultat.

2. Le fil unificateur : un seul appareil, trois substrats

Chaque substrat produit une trajectoire d’états. On la discrétise grossièrement (un choix documenté, propre à chaque substrat — pas une constante cachée), on estime une TPM état-à-état (ict.tpm_estimation, le pont d’ICT-6), puis on applique la même batterie d’émergence causale (ict.causal_emergence) via le module de synthèse ict.synthesis livré en amont (#4948) :

  • information effective (EI, bits) et effectiveness (déterminisme − dégénérescence, sans échelle) ;
  • complexité émergente (EC) le long du chemin d’échelles glouton (greedy_apportionment).

Le contrôle sans complaisance. Pour chaque substrat, on compare la mesure réelle à un contrôle dégénéré : la trajectoire dont on a permuté l’ordre temporel (shuffle_states), ce qui conserve exactement le multi-ensemble d’états visités mais détruit la dynamique. Une émergence n’est créditée que si elle dépasse ce contrôle — sinon, elle n’est qu’un artefact du réservoir d’états (des degrés de liberté), pas de l’ordre. C’est la discipline d’ICT-8/9/11 généralisée à trois substrats à la fois.

Runtime borné (choix assumé). Le chemin d’échelles de greedy_apportionment explore toutes les paires d’états à chaque pas (coût ~\(O(k^2)\) par échelle, \(k\) = nombre d’états distincts). On garde donc une discrétisation grossière (≈ 6–12 états par substrat) et des trajectoires sous-échantillonnées : l’exécution complète tient en moins de deux minutes tout en laissant chaque gate discriminant. Une discrétisation fine (ex. tuple(config) brut du tri) ferait exploser \(k\) sans rien ajouter au verdict.

import sys, os
sys.path.insert(0, os.getcwd())

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline

from ict import synthesis as S
from ict import sorting_metrics as sm
from ict.self_sorting import SelfSortingArray
from ict.bistable import GrazingModel
from ict import strategic_morphodynamics as SM
from ict import time_arrow as ta
from ict import stake
from ict import agency

# --- parametres bornes (voir la note "Runtime borne" ci-dessus) ---
N_SHUF = 100            # controle degenere pour les gates 1-2 (une graine)
N_SHUF_SEED = 30        # controle par graine pour le gate 3 (multi-graines)
SEEDS = (0, 1, 7, 42, 99)   # graines du gate 3 (regime = alea de generation)
SEED0 = SEEDS[0]        # graine representative des gates 1-2


def traj_S1(seed, n_runs=6, max_steps=1500):
    """S1 - tri auto-organise : etat = nombre d'inversions (coarse, ICT-2/6)."""
    states = []
    for r in range(n_runs):
        rng = np.random.default_rng(seed + r)
        arr = SelfSortingArray(list(rng.permutation(6)), seed=seed + r)
        arr.run(max_steps=max_steps, record=True)
        states.extend(sm.inversion_count(c) for c in arr.probe.values)
    return states


def traj_S2(seed, T=1500):
    """S2 - paysage bistable (modele de paturage de May, ICT-8) : SDE pres du pli,
    etat = decile du scalaire de biomasse."""
    gm = GrazingModel()
    c = 0.92 * gm.find_fold()          # sous le pli : deux etats stables coexistent
    xs = gm.simulate_sde(c=c, x0=8.0, sigma=0.55, dt=0.02, T=T, seed=seed)
    lo, hi = np.percentile(xs, 1), np.percentile(xs, 99)
    bins = np.linspace(lo, hi, 11)     # 10 deciles -> ~11 etats
    return [int(v) for v in np.clip(np.digitize(xs, bins), 0, 10)]


def traj_S3(seed, n_runs=10, n_steps=300):
    """S3 - replicateur strategique (IPD/Axelrod, ICT-13) : etat = strategie dominante."""
    rng = np.random.default_rng(seed)
    strat = SM.make_strategies(rng)
    A = SM.payoff_matrix(strat, rng=rng)
    states = []
    for _ in range(n_runs):
        x0 = rng.dirichlet(np.ones(len(strat)))
        traj = SM.replicator_trajectory(A, x0, n_steps=n_steps)
        states.extend(int(np.argmax(row)) for row in traj)
    return states




# --- substrat S4 : activations SAE d'un LLM (strate 5, ICT-21/22) ---
# Etats discrets binarises lus depuis la trace GPU-extraite (cache .npz, zero GPU ici).
# Panel K=4 features (ICT-22 Gate 13) ; "seed" indexe le jeu de prompts pour la reproductibilite.
NPZ_TRAINED = "traces/ict21_sae_layer16_trained.npz"   # Qwen3.5-9B layer 16, k=50
PANEL_S4 = [65025, 19350, 5437, 24514]                  # ICT-22 panel (K=4)
PROMPT_SETS = ["code_python", "dialogue", "math", "narrative_en", "prose_fr"]


def traj_S4(seed, sets=None):
    """S4 - substrat LLM : etats discrets binarises des activations SAE (ICT-21/22).

    Par defaut (sets=None) on concatene un seul jeu choisi par la graine ; passer
    sets=PROMPT_SETS pour pooler les 5 jeux en une longue trajectoire (style ICT-22 Gate 13).
    """
    chosen = [PROMPT_SETS[seed % len(PROMPT_SETS)]] if sets is None else sets
    return S.sae_substrate_states(NPZ_TRAINED, PANEL_S4, sets=chosen, q=0.5)

print("Appareil de mesure unique : trajectoire -> TPM etat-a-etat -> batterie d'emergence causale.")
print(f"Controle degenere : permutation temporelle (marginales d'etats conservees, dynamique detruite).")
print(f"Graines gate 3 : {SEEDS} | n_shuffles gates 1-2 = {N_SHUF}, gate 3 = {N_SHUF_SEED}")
Appareil de mesure unique : trajectoire -> TPM etat-a-etat -> batterie d'emergence causale.
Controle degenere : permutation temporelle (marginales d'etats conservees, dynamique detruite).
Graines gate 3 : (0, 1, 7, 42, 99) | n_shuffles gates 1-2 = 100, gate 3 = 30

3. Trois substrats, trois strates

On instancie les trois trajectoires avec la graine représentative SEED0. Chacune vient d’une strate différente de la série et n’a, a priori, rien à voir avec les autres.

S1 — le tri auto-organisé (strate 1)

Le banc d’entrée de la série (ICT-2/ICT-6) : plusieurs tris distribués, résumés par leur nombre d’inversions à chaque pas (macro-état grossier, borné par \(n(n-1)/2\)).

s1 = traj_S1(SEED0)
print(f"S1 (tri)          longueur={len(s1):5d}  etats distincts={len(set(s1)):3d}  "
      f"(min={min(s1)}, max={max(s1)})")
S1 (tri)          longueur=  216  etats distincts= 12  (min=0, max=11)

S2 — le paysage bistable (strate 2)

Le modèle de pâturage de May (ICT-8), simulé en SDE juste sous le pli (\(c = 0.92\,c_\text{fold}\)) : deux bassins coexistent et le bruit provoque des bascules occasionnelles. L’état est le décile de la biomasse.

s2 = traj_S2(SEED0)
print(f"S2 (bistable)     longueur={len(s2):5d}  etats distincts={len(set(s2)):3d}  "
      f"(deciles occupes)")
S2 (bistable)     longueur= 1500  etats distincts= 11  (deciles occupes)

S3 — le réplicateur stratégique (strate 3)

Le dilemme du prisonnier itéré sous dynamique de réplicateur (ICT-13) : plusieurs trajectoires depuis des populations initiales aléatoires, résumées par la stratégie dominante (argmax des fréquences) à chaque pas.

s3 = traj_S3(SEED0)
print(f"S3 (replicateur)  longueur={len(s3):5d}  etats distincts={len(set(s3)):3d}  "
      f"(strategies dominantes)")
S3 (replicateur)  longueur= 3010  etats distincts=  6  (strategies dominantes)

4. Le même appareil appliqué aux trois — batterie + contrôle dégénéré

On calcule, pour chaque substrat, la batterie réelle (trajectory_battery) et le contrôle dégénéré moyenné sur N_SHUF permutations temporelles (shuffled_baseline). Le gain d’émergence est la différence réel − contrôle ; il est crédité si le gain d’EC dépasse l’écart-type du contrôle.

subs = {"S1_tri": s1, "S2_bistable": s2, "S3_replicateur": s3}
rng = np.random.default_rng(2025)

detail = {}
for name, st in subs.items():
    real = S.trajectory_battery(st)
    base = S.shuffled_baseline(st, rng, n_shuffles=N_SHUF)
    detail[name] = {"real": real, "base": base}

print(f"{'substrat':16s} {'n_etats':>7s} {'EI_reel':>8s} {'EI_shuf':>8s} "
      f"{'EC_reel':>8s} {'EC_shuf':>8s} {'EC_std':>7s}")
print("-" * 72)
for name, d in detail.items():
    r, b = d["real"], d["base"]
    print(f"{name:16s} {r['n_states']:7d} {r['effective_information']:8.3f} "
          f"{b['effective_information']:8.3f} {r['emergent_complexity']:8.3f} "
          f"{b['emergent_complexity']:8.3f} {b['emergent_complexity_std']:7.3f}")
substrat         n_etats  EI_reel  EI_shuf  EC_reel  EC_shuf  EC_std
------------------------------------------------------------------------
S1_tri                12    2.538    0.672    6.127    2.077   2.174
S2_bistable           11    2.822    0.106    6.255    5.240   0.818
S3_replicateur         6    2.413    0.029    4.415    2.341   0.781

5. Gate 1 — l’émergence survit-elle au contrôle dégénéré ?

Critère falsifiable. Pour chaque substrat, on crédite une émergence dynamique si le gain de complexité émergente dépasse le bruit du contrôle : \(z = (\text{EC}_\text{réel} - \text{EC}_\text{shuffle}) / \sigma_\text{shuffle} > 1\). Si aucun substrat ne passe, l’appareil ne mesure que du réservoir d’états — pas de la dynamique.

summary = {}
print(f"{'substrat':16s} {'EI_gain':>8s} {'EC_gain':>8s} {'z(EC)':>7s}  credite")
print("-" * 56)
for name, d in detail.items():
    r, b = d["real"], d["base"]
    ei_gain = r["effective_information"] - b["effective_information"]
    ec_gain = r["emergent_complexity"] - b["emergent_complexity"]
    ec_std = b["emergent_complexity_std"]
    z = ec_gain / ec_std if ec_std > 1e-12 else float("inf")
    credited = z > 1.0
    summary[name] = {"ei_real": r["effective_information"],
                     "ec_real": r["emergent_complexity"],
                     "ei_gain": ei_gain, "ec_gain": ec_gain,
                     "z": z, "credited": credited}
    print(f"{name:16s} {ei_gain:+8.3f} {ec_gain:+8.3f} {z:7.2f}  {credited}")

n_credited = sum(v["credited"] for v in summary.values())
print(f"\nGate 1 : {n_credited}/3 substrats creditent une emergence dynamique (z > 1).")

# --- figure : reel vs controle degenere (information effective) ---
names = list(subs)
x = np.arange(len(names))
fig, ax = plt.subplots(figsize=(7, 3.4))
ax.bar(x - 0.2, [detail[n]["real"]["effective_information"] for n in names], 0.4,
       label="reel", color="#2c7fb8")
ax.bar(x + 0.2, [detail[n]["base"]["effective_information"] for n in names], 0.4,
       label="controle degenere (shuffle)", color="#c0c0c0")
ax.set_xticks(x); ax.set_xticklabels(names); ax.set_ylabel("information effective (bits)")
ax.set_title("Gate 1 - la dynamique bat-elle son controle degenere ?"); ax.legend()
plt.tight_layout(); plt.show()
substrat          EI_gain  EC_gain   z(EC)  credite
--------------------------------------------------------
S1_tri             +1.865   +4.049    1.86  True
S2_bistable        +2.716   +1.015    1.24  True
S3_replicateur     +2.384   +2.074    2.66  True

Gate 1 : 3/3 substrats creditent une emergence dynamique (z > 1).

Lecture du gate 1

L’appareil mesure de la dynamique, pas du réservoir. À la graine représentative, les trois substrats créditent une émergence dynamique (\(z > 1\)) : S1 \(z\)=1.86 (gain d’EC +4.05), S2 \(z\)=1.24 (+1.02), S3 \(z\)=2.66 (+2.07). Autrement dit, l’ordre temporel réel bat son contrôle dégénéré dans chaque cas — l’instrument capte bien la dynamique, pas seulement la taille du multi-ensemble d’états. Premier indice cependant : les gains d’EC sont très inégaux (S1 ≫ S3 > S2) alors que l’information effective, elle, place S2 en tête. « Combien d’émergence » et « combien d’intégration » ne classent déjà pas les substrats pareil — ce que le gate 2 va trancher.

6. Gate 2 — un scalaire d’intégration transfère-t-il ? (une graine)

Critère falsifiable. Si un « scalaire d’intégration » gouvernait les trois substrats, ils seraient classés dans le même ordre par l’information effective réelle (ei_real) et par le gain d’émergence crédité (ec_gain). On mesure la concordance de rangs (\(\tau\) de Kendall) : consistent = vrai seulement si \(\tau \approx +1\). Un \(\tau < 1\) falsifie l’existence d’un scalaire transférable — dès une seule graine.

rc = S.rank_consistency(summary, "ei_real", "ec_gain")
print("Classement par EI reelle  :", rc["order_by_ei_real"])
print("Classement par gain d'EC   :", rc["order_by_ec_gain"])
print(f"tau de Kendall (EI vs EC_gain) = {rc['kendall_tau']:+.3f}  ->  consistent = {rc['consistent']}")

# second couple de mesures, pour montrer que le desaccord n'est pas un artefact du choix
rc2 = S.rank_consistency(summary, "ei_real", "ec_real")
print(f"tau de Kendall (EI vs EC_reel) = {rc2['kendall_tau']:+.3f}  ->  consistent = {rc2['consistent']}")

fig, ax = plt.subplots(figsize=(7, 3.0))
metrics = {"EI reelle": [summary[n]["ei_real"] for n in names],
           "gain d'EC": [summary[n]["ec_gain"] for n in names]}
w = 0.38
for i, (lab, vals) in enumerate(metrics.items()):
    v = np.array(vals); v = v / (np.abs(v).max() + 1e-12)   # normalise pour comparer les rangs
    ax.bar(x + (i - 0.5) * w, v, w, label=lab)
ax.set_xticks(x); ax.set_xticklabels(names)
ax.set_ylabel("mesure (normalisee)"); ax.set_title("Gate 2 - deux mesures, deux classements ?")
ax.legend(); plt.tight_layout(); plt.show()
Classement par EI reelle  : ['S2_bistable', 'S1_tri', 'S3_replicateur']
Classement par gain d'EC   : ['S1_tri', 'S3_replicateur', 'S2_bistable']
tau de Kendall (EI vs EC_gain) = -0.333  ->  consistent = False
tau de Kendall (EI vs EC_reel) = +1.000  ->  consistent = True

Lecture du gate 2

Le scalaire transférable est un mirage de magnitude. Deux mesures brutes — information effective réelle et complexité émergente réelle — classent les trois substrats à l’identique (\(\tau\) = +1.00) : de quoi croire à un « scalaire d’intégration » commun. Mais dès qu’on classe par le gain crédité contre le contrôle dégénéré (ec_gain), l’ordre s’inverse (\(\tau\) = −0.33, consistent = False) : S2 domine en EI réelle mais tombe dernier en gain crédité, tandis que S1 mène par le gain mais reste au milieu en EI. La concordance parfaite était donc un artefact de magnitude (des systèmes plus grands ont mécaniquement plus d’EI et d’EC bruts) ; ce qui survit au contrôle sans complaisance — la seule quantité que toute la série accepte de créditer — ne transfère pas. L’hypothèse du scalaire unique est falsifiée dès une seule graine.

7. Gate 3 — la créditation est-elle robuste au régime ? (multi-graines)

Critère falsifiable. On répète toute la chaîne sur SEEDS graines (chaque graine = un régime d’aléa de génération). Pour chacune : l’ensemble crédité et la concordance de rangs \(\tau\). Si un scalaire transférait vraiment, consistent serait stablement vrai et l’ensemble crédité stable. S’ils varient d’une graine à l’autre, alors non seulement aucun scalaire ne transfère, mais la créditation elle-même est régime-dépendante — l’écho, un cran plus haut, du fil ICT-10 → ICT-13.

rows = []
for seed in SEEDS:
    ss = {"S1_tri": traj_S1(seed), "S2_bistable": traj_S2(seed), "S3_replicateur": traj_S3(seed)}
    r = np.random.default_rng(1000 + seed)
    summ = S.cross_substrate_summary(ss, r, n_shuffles=N_SHUF_SEED)
    rcx = S.rank_consistency(summ, "ei_real", "ec_gain")
    credited = tuple(sorted(k for k, v in summ.items() if v["credited"]))
    rows.append({"seed": seed, "tau": rcx["kendall_tau"],
                 "consistent": rcx["consistent"], "credited": credited})

print(f"{'graine':>6s} {'tau':>6s} {'consistent':>11s}  ensemble credite")
print("-" * 60)
for row in rows:
    print(f"{row['seed']:6d} {row['tau']:+6.2f} {str(row['consistent']):>11s}  "
          f"{{{', '.join(row['credited']) if row['credited'] else '(aucun)'}}}")

taus = [row["tau"] for row in rows]
n_consistent = sum(row["consistent"] for row in rows)
credited_sets = {row["credited"] for row in rows}
print(f"\ntau observes : {sorted(set(round(t, 2) for t in taus))}")
print(f"consistent = vrai sur {n_consistent}/{len(rows)} graines")
print(f"nombre d'ensembles credites distincts : {len(credited_sets)}")

fig, ax = plt.subplots(figsize=(7, 3.0))
ax.axhline(1.0, color="#2ca25f", ls="--", lw=1, label="tau=+1 (transfert parfait)")
ax.axhline(0.0, color="#999999", ls=":", lw=1)
ax.plot(range(len(SEEDS)), taus, "o-", color="#de2d26", label="tau (EI vs gain d'EC)")
ax.set_xticks(range(len(SEEDS))); ax.set_xticklabels([str(s) for s in SEEDS])
ax.set_xlabel("graine (regime)"); ax.set_ylabel("tau de Kendall"); ax.set_ylim(-1.15, 1.15)
ax.set_title("Gate 3 - le transfert est-il stable d'un regime a l'autre ?"); ax.legend()
plt.tight_layout(); plt.show()
graine    tau  consistent  ensemble credite
------------------------------------------------------------
     0  -0.33       False  {S1_tri, S3_replicateur}
     1  +0.33       False  {S2_bistable, S3_replicateur}
     7  +0.33       False  {S2_bistable, S3_replicateur}
    42  +1.00        True  {S2_bistable}
    99  -0.33       False  {S2_bistable, S3_replicateur}

tau observes : [-0.33, 0.33, 1.0]
consistent = vrai sur 1/5 graines
nombre d'ensembles credites distincts : 3

Lecture du gate 3

Non seulement rien ne transfère, mais la créditation elle-même est régime-dépendante. Sur cinq graines (cinq régimes d’aléa), consistent n’est vrai que 1 fois sur 5 — et cette unique « cohérence » (graine 42) est dégénérée : elle ne crédite que S2, si bien qu’un classement à un seul élément est trivialement « cohérent ». L’ensemble crédité varie d’une graine à l’autre — trois ensembles distincts : {S1, S3} (graine 0), {S2, S3} (graines 1, 7, 99), {S2} seul (graine 42) — et \(\tau\) oscille dans {−0.33, +0.33, +1.0}. Concrètement : S2 (bistable) est crédité 4 fois sur 5 (4/5), S3 (réplicateur) presque partout (4/5), S1 (tri) une seule fois (1/5). La question « quels substrats émergent ? » n’a donc pas de réponse absolue — elle dépend du régime. C’est exactement le motif que la série a rencontré à l’intérieur de chaque substrat — ICT-10 (\(\hat p\) utile en régime \(a<0\) seulement), ICT-11 (aucune granularité privilégiée), ICT-12 (\(\hat p\) ne paie que dans certains environnements), ICT-13 (stratégie stable dans un paysage donné seulement) — reproduit ici un cran plus haut, entre substrats.

8. Gate 4 — la convergence Φ/F/K survit-elle à un quatrième substrat ? (capstone strate 5)

Les gates 1 à 3 ne créditaient l’émergence que sur un scalaire, l’information effective (EI → EC gain, leg Φ). Le capstone de la strate 4 (ICT-15) avait ajouté deux jambes — la surprise transitionnelle F et la compressibilité K — et posé la question de leur convergence. Ici, nous la rejouons en intégrant un quatrième substrat : les activations d’un LLM (Qwen3.5-9B, couche 16, projetées par une SAE). L’appareil emergence_gain retourne les trois jambes en un appel, sous le même contrôle dégénéré (permutation temporelle) — condition pour que le τ de Kendall soit interprétable.

Nommage des substrats. Le banc nomme ses substrats par leur nature, pas par un index : tri auto-organisé (ICT-2), bistable / modèle de pâturage de May (ICT-8), réplicateur d’Axelrod (ICT-13), LLM-SAE = activations SAE d’un Qwen3.5-9B couche 16 (ICT-21/22). Le cinquième, le champ Gray-Scott (ICT-9/18/19), est un champ réaction-diffusion continu traité à part (gate 6). ⚠ Surcharge terminologique : l’index « S4 » désigne le substrat LLM ici et dans ICT-21/22, mais le Gray-Scott dans ICT-18/19 — d’où le choix de nommer par nature pour lever l’ambiguïté.

# Gate 4 : convergence Phi/F/K sur 4 substrats discrets (lift ICT-22 Gate 13)
s4 = traj_S4(SEED0, sets=PROMPT_SETS)            # substrat LLM : 5 jeux poolles
subs_4 = {"S1_tri": s1, "S2_bistable": s2, "S3_replicateur": s3, "S4_LLM": s4}

print(f"{'substrat':16s} {'etats':>5s} {'Phi(ec)':>9s} {'F(fe)':>8s} {'K(k)':>7s}")
print("-" * 52)
gains_table = {}
for name, st in subs_4.items():
    g = S.emergence_gain(st, np.random.default_rng(2025), n_shuffles=N_SHUF)
    gains_table[name] = g
    print(f"{name:16s} {g['n_states']:5d} {g['ec_gain']:+9.3f} {g['fe_gain']:+8.3f} {g['k_gain']:+7.3f}")

# tau de Kendall par paire : un scalaire ordonne-t-il les 4 substrats comme un autre ?
print("\n--- Gate 4 : convergence (tau de Kendall par paire) ---")
for ka, kb in [("ec_gain", "fe_gain"), ("ec_gain", "k_gain"), ("fe_gain", "k_gain")]:
    rc = S.rank_consistency(gains_table, ka, kb)
    tag = "CONSISTENT" if rc["consistent"] else "divergent"
    print(f"  tau({ka:>8s} vs {kb:<8s}) = {rc['kendall_tau']:+.3f}  ->  {tag}")

fig, ax = plt.subplots(figsize=(7.5, 3.6))
x = np.arange(len(names4 := list(subs_4))); w = 0.26
ax.bar(x - w, [gains_table[n]["ec_gain"] for n in names4], w, label=r"$\Phi$ (ec_gain)", color="#2c7fb8")
ax.bar(x,     [gains_table[n]["fe_gain"] for n in names4], w, label=r"F (fe_gain)", color="#e6550d")
ax.bar(x + w, [gains_table[n]["k_gain"]  for n in names4], w, label=r"K (k_gain)", color="#31a354")
ax.axhline(0, color="#555", lw=0.8); ax.set_xticks(x); ax.set_xticklabels(names4)
ax.set_ylabel("gain reel vs controle degenere")
ax.set_title(r"Gate 4 — convergence $\Phi$/F/K sur 4 substrats")
ax.legend(loc="upper right", fontsize=9); plt.tight_layout(); plt.show()
substrat         etats   Phi(ec)    F(fe)    K(k)
----------------------------------------------------
S1_tri              12    +4.049   +2.136  +0.341
S2_bistable         11    +1.094   +1.515  +0.680
S3_replicateur       6    +2.133   +2.087  +0.941
S4_LLM              13    +0.271   +0.120  +0.232

--- Gate 4 : convergence (tau de Kendall par paire) ---
  tau( ec_gain vs fe_gain ) = +1.000  ->  CONSISTENT
  tau( ec_gain vs k_gain  ) = +0.333  ->  divergent
  tau( fe_gain vs k_gain  ) = +0.333  ->  divergent

Lecture du gate 4

Les trois jambes covarient partiellement. Les barres Φ (bleu) et F (orange) suivent le même ordre — tri > réplicateur > bistable > LLM — d’où un τ de Kendall Φ↔︎F = +1.00 (transfert parfait). Mais K (vert) ordonne autrement (réplicateur et bistable au-dessus du tri) : Φ↔︎K et F↔︎K divergent (τ ≈ +0.33). Le LLM, quatrième substrat, est systématiquement le plus faible sur les trois jambes — ses activations SAE, bien que réelles, ne structurent pas la trajectoire au point de battre largement un contrôle dégénéré. Verdict : aucun triplet de scalaires ne converge vers un invariant unique ; la convergence apparente Φ↔︎F tient à ce que ces deux jambes mesurent une même chose (la structure causale de la trajectoire), et K la mesure autrement. Le quatrième substrat renforce le verdict d’ICT-22 : ce n’est pas un scalaire qui transfère, c’est la méthode.

9. Gate 5 — l’irréversibilité, jambe temporelle de l’émergence (ICT-18)

Une trajectoire émergente produit-elle de l’entropie, c’est-à-dire est-elle temporellement asymétrique ? ICT-18 a construit cet appareil : estimer la matrice de transition, mesurer sa production d’entropie σ (l’écart à l’équilibre détaillé), et la distance entre la chaîne réelle et sa projection réversibilisée (la chaîne la plus proche qui satisfait le bilan détaillé). Un substrat réversible (σ ≈ 0, dist ≈ 0) ne « dégrade » pas l’information ; un substrat irréversible, si.

# Gate 5 : reversibilisation (time_arrow) par substrate
print(f"{'substrat':16s} {'sigma_real':>11s} {'dist(reel,rev)':>15s}")
print("-" * 46)
rev_table = {}
for name, st in subs_4.items():
    rev = ta.compare_real_reversed_reversibilized(st)
    rev_table[name] = rev
    print(f"{name:16s} {rev['sigma_real']:11.4f} {rev['dist_real_vs_reversibilized']:15.4f}")

fig, ax = plt.subplots(figsize=(7.5, 3.4))
sig = [rev_table[n]["sigma_real"] for n in names4]
dist = [rev_table[n]["dist_real_vs_reversibilized"] for n in names4]
x = np.arange(len(names4)); w = 0.38
ax.bar(x - w/2, sig, w, label=r"$\sigma_{real}$ (production d'entropie)", color="#756bb1")
ax.bar(x + w/2, dist, w, label="dist(reel, reversibilise)", color="#99d8c9")
ax.set_xticks(x); ax.set_xticklabels(names4); ax.set_yscale("symlog", linthresh=1e-3)
ax.set_ylabel("asymetrie temporelle (echelle symlog)")
ax.set_title("Gate 5 — reversibilisation : tri irreversibles, bistable reversible")
ax.legend(loc="upper right", fontsize=9); plt.tight_layout(); plt.show()
substrat          sigma_real  dist(reel,rev)
----------------------------------------------
S1_tri                5.6643          2.3490
S2_bistable           0.0000          0.0000
S3_replicateur        0.0196          0.0205
S4_LLM                0.0889          1.1774

Lecture du gate 5

L’asymétrie temporelle discrimine fortement les substrats, à l’inverse des scalaires Φ/F/K. Le tri (S1) domine très largement (σ ≈ 5.66, échelle symlog) — c’est attendu : un tri est directionnel, il court vers l’ordre, il n’est pas symétrique sous renversement du temps. Le bistable (S2) est quasi-nul (σ ≈ 0) : son SDE de pâturage, près du pli, satisfait approximativement le bilan détaillé — il est temporellement réversible. Le réplicateur et le LLM restent faibles. Leçon : la jambe temporelle de l’émergence est orthogonale à la jambe causale — un substrat peut être fortement émergent (Φ élevé) et faiblement irréversible (bistable), ou inversement. Aucun substrat ne maximise tout à la fois.

10. Gate 6 — l’ENJEU et la frontière de l’agence (ICT-19)

La strate 5 a introduit une cinquième jambe : l’ENJEU (stake) — un système émerge-t-il comme agent au sens où, après une perturbation (do), il revient de lui-même à son attracteur plutôt que de dériver ? ICT-19 a d’abord tenté un scalaire stake_index sur les états discrets. Il échoue sur le substrat champ (Gray-Scott) — et c’est l’enseignement méthodologique le plus important de la strate 5 : quand un instrument échoue sur un substrat, on change d’instrument, pas de conclusion. ICT-19-Raffinement a donc basculé vers une mesure en espace de champ, repair_gain, qui compare la réparation après ablation (réaction-diffusion réelle) à la pure diffusion.

# Gate 6 : frontiere de l'instrument ENJEU (scalaire sur discrets vs champ sur Gray-Scott)
# Demo ICT-19 : le scalaire stake_index discrimine restoring vs drift (controle).
i_rest, i_drift = stake.demo_contrast(steps=50, seed=42)
print(f"demo_contrast (controle) : I_stake(restoring) = {i_rest:+.4f}")
print(f"                            I_stake(drift)      = {i_drift:+.4f}")
print(f"                            delta               = {i_rest - i_drift:+.4f}")
print()
print("Le scalaire stake_index CREDITE les attracteurs discrets (S1/S2/S3) :")
print("  S1 tri = +1.00 | S2 bistable = +1.00 | S3 replicateur (coop) = +0.68  (cf ICT-19)")
print()
print("MAIS il ECCHOUE sur le substrat champ Gray-Scott (I_stake = -0.51 = sous le drift).")
print("ICT-19-Raffinement a donc change d'instrument pour le champ :")
print("  agency.repair_gain (Gray-Scott, 5 ablations) = +0.82 +- 0.27   <-- REPARATION ACTIVE")
print("  (controle : pure diffusion)                  = +0.00            <-- pas d'agence")
print()
print("=> L'ENJEU n'est pas un scalaire universel : il exige le bon espace de mesure (discret vs champ).")
demo_contrast (controle) : I_stake(restoring) = +0.9894
                            I_stake(drift)      = -1.0000
                            delta               = +1.9894

Le scalaire stake_index CREDITE les attracteurs discrets (S1/S2/S3) :
  S1 tri = +1.00 | S2 bistable = +1.00 | S3 replicateur (coop) = +0.68  (cf ICT-19)

MAIS il ECCHOUE sur le substrat champ Gray-Scott (I_stake = -0.51 = sous le drift).
ICT-19-Raffinement a donc change d'instrument pour le champ :
  agency.repair_gain (Gray-Scott, 5 ablations) = +0.82 +- 0.27   <-- REPARATION ACTIVE
  (controle : pure diffusion)                  = +0.00            <-- pas d'agence

=> L'ENJEU n'est pas un scalaire universel : il exige le bon espace de mesure (discret vs champ).

Lecture du gate 6

La frontière de l’ENJEU n’est pas une limite du concept mais de l’instrument. Le scalaire stake_index crédite sans ambiguïté les substrats discrets (tri, bistable, réplicateur : l’attracteur se restaure après do), et rejette à juste titre un drift de contrôle (delta ≈ +2). Mais le Gray-Scott — un champ réaction-diffusion continu — n’a pas d’état discret unique à kick : le scalaire lit −0.51, faussement « pas d’agence ». La vérité, mesurée en espace de champ (repair_gain = +0.82 ± 0.27, significativement au-dessus du contrôle diffusion ≈ 0), est que le Gray-Scott répare activement sa structure après ablation. Verdict : la batterie de la série ne cherche pas un invariant universel — elle demande à chaque substrat le bon espace où lire son émergence, et ne crédite rien qui ne batte son contrôle. Là est l’honnêteté de la méthode.

11. Verdict sans complaisance

Réponse à la question centrale : non, aucun scalaire d’intégration ne transfère entre substrats — et le cinquième substrat le confirme.

  • Les scalaires bruts co-varient par paires : sur quatre substrats, Φ et F suivent le même ordre (τ = +1.00). Mais K diverge (τ ≈ +0.33), et la jambe temporelle (irréversibilité) classe les substrats de façon orthogonale (le tri est le plus irréversible, le bistable le plus réversible).
  • L’émergence créditée — celle qui bat son contrôle dégénéré — change de membres selon le régime (gate 3) et reste régime-bornée.
  • Le substrat LLM, le plus riche en représentations, est le plus faible sur les trois jambes Φ/F/K : la richesse d’un substrat ne garantit pas une dynamique émergente lisible par l’appareil.
  • L’ENJEU (gate 6) montre que la frontière n’est pas le concept mais l’instrument : un scalaire discret crédite trois substrats, mais il faut un instrument de champ pour révéler l’agence réparatrice du Gray-Scott (+0.82 ± 0.27).

Ce qui transfère, c’est la méthode, pas le nombre. Le même appareil — estimer la structure causale le long de la trajectoire propre d’un système, ne créditer une émergence qu’au contraste d’un contrôle dégénéré, et changer d’instrument quand un substrat résiste — s’applique aux cinq substrats et rend à chaque fois un verdict falsifiable. Mais ces verdicts sont régime-bornés et substrat-bornés : jamais un invariant numérique unique.

C’est la thèse d’ICT jouée au niveau du capstone. L’invariant de la série n’est pas Φ, ni l’information effective, ni un « niveau d’intégration », ni l’irréversibilité, ni l’ENJEU : c’est la discipline de mesure — mesurer ce que le système fait vraiment, dans le bon espace, et ne rien créditer qui ne batte son propre contrôle dégénéré. Le livrable de ce capstone est cette honnêteté-là, étendue de trois substrats à cinq.

12. Exercices

Trois exercices à compléter. Convention C.1 : stub sans erreur volontaire (pass / return None / # TODO etudiant) — le notebook s’exécute de bout en bout même non complété. Chaque exercice est une extension falsifiable de la synthèse.

Exercice 1 — un quatrième substrat

Ajoutez un substrat issu d’une autre strate — par exemple le champ de réaction-diffusion d’ICT-9 résumé par une statistique scalaire discrétisée, ou une trajectoire de \(\Phi\) d’ICT-1. Rejoint-il l’ensemble crédité ? Déplace-t-il le classement (gate 2) ?

  • Étape 1 : construire une trajectoire traj_s4 (liste d’états discrets grossiers).
  • Étape 2 : appeler S.emergence_gain(traj_s4, rng, n_shuffles=N_SHUF) et comparer credited.
# Exercice 1 - a completer
traj_s4 = None  # TODO etudiant : liste d'etats discrets d'un 4e substrat (ICT-9 ou ICT-1)
if traj_s4 is not None:
    g4 = S.emergence_gain(traj_s4, np.random.default_rng(4), n_shuffles=N_SHUF)
    print(f"S4 : credite={g4['credited']}  ec_gain={g4['ec_gain']:+.3f}  n_etats={g4['n_states']}")
else:
    print("Exercice 1 a completer : definir traj_s4 (liste d'etats discrets).")
Exercice 1 a completer : definir traj_s4 (liste d'etats discrets).

Exercice 2 — robustesse à la discrétisation

Le verdict dépend-il du grain de discrétisation ? Reprenez S2 (le bistable) avec un binning plus fin (par ex. 20 bins au lieu de 10) et un plus grossier (5 bins), et vérifiez si son statut credited et le \(\tau\) du gate 2 survivent. Une discrétisation qui fabrique l’émergence est un piège que la série dénonce (ICT-7/9).

  • Étape 1 : écrire traj_S2_grain(seed, n_bins) (copie de traj_S2 paramétrée).
  • Étape 2 : recalculer emergence_gain pour n_bins ∈ {5, 10, 20} et comparer.
# Exercice 2 - a completer
def traj_S2_grain(seed, n_bins):
    """TODO etudiant : copier traj_S2 en parametrant le nombre de bins."""
    return None  # remplacer par la trajectoire discretisee a n_bins deciles

resultats = None  # TODO etudiant : dict {n_bins: emergence_gain(...)}
if resultats is not None:
    for nb, g in resultats.items():
        print(f"n_bins={nb:2d} : credite={g['credited']}  ec_gain={g['ec_gain']:+.3f}")
else:
    print("Exercice 2 a completer : balayer n_bins in {5, 10, 20}.")
Exercice 2 a completer : balayer n_bins in {5, 10, 20}.

Exercice 3 — un contrôle dégénéré plus dur

Le contrôle par permutation totale détruit tout l’ordre temporel. Un contrôle plus exigeant préserve l’ordre à courte portée : le block-shuffle permute des blocs contigus de longueur \(b\) (il garde la dynamique locale, casse la globale). Sous ce contrôle plus dur, le gain crédité rétrécit-il ? De combien ?

  • Étape 1 : écrire block_shuffle(states, rng, b) (permutation de blocs de longueur b).
  • Étape 2 : recalculer le gain d’EC de S1/S2/S3 contre ce contrôle et comparer au shuffle total.
# Exercice 3 - a completer
def block_shuffle(states, rng, b=10):
    """TODO etudiant : decouper en blocs de longueur b, permuter l'ordre des blocs."""
    return None  # remplacer par la trajectoire block-shufflee

# Indice : comparer S.trajectory_battery(block_shuffle(...)) a S.trajectory_battery(states)
print("Exercice 3 a completer : implementer block_shuffle et comparer au controle total.")
Exercice 3 a completer : implementer block_shuffle et comparer au controle total.

Pour aller plus loin


ICT-Synthèse — un seul appareil de mesure, cinq substrats. Epic #4588. Voir ICT-0-Framing pour le cadrage de la série.

Retour au sommet