ICT-31 — Le contraste mesuré à trois substrats : bistable, Gray-Scott, Jeu de la Vie
Série ICT (Integrated Causal Trajectories, Epic #4588) — issue #5726, point 2 du dispatch du 2026-08-16 : « Contraste à trois substrats : GOL vs S2 bistable vs S5 Gray-Scott, passés dans la même batterie ICT. C’est le cœur du livrable : la thèse est que GOL se distingue, et un notebook qui ne mesure qu’un substrat ne peut pas l’établir. »
ICT-Life-SubstratCertifie a livré le certificat de calibration du substrat Jeu de la Vie, puis a tabulé le contraste des trois substrats (table de la section 4). Ce notebook fait la step suivante : il mesure ce contraste — les trois substrats passent par les mêmes protocoles, avec les mêmes fonctions du package ict, et les nombres parlent d’eux-mêmes.
Les trois substrats (tous déjà outillés par la série — ICT-8 pour le bistable, ICT-9 pour Gray-Scott, ICT-Life pour GOL) :
#
Substrat
Dimension
Dynamique
Rôle dans la batterie
S2
Paturage de May (bistable)
0-D (scalaire x)
ODE à bifurcation pli
attracteur ponctuel, mémoire de chemin (hystérésis)
S5
Gray-Scott (réaction-diffusion)
2-D (champs U,V)
morphogenèse dissipative de Turing
régénération locale (taches mitotiques de Pearson)
GOL
Jeu de la Vie (B3/S23)
2-D (grille booléenne)
automate cellulaire conservatif en translation
transport d’information localisée (gliders)
Statut épistémique — Sans verdict à ce jour : aucune ligne de la matrice de dissociations ne concerne ce notebook ; son statut épistémique sera porté par la matrice le cas échéant.
La question — trois modes de relation à l’information
La thèse à établir n’est pas « GOL est meilleur » : c’est que les trois substrats entretiennent des relations structurellement différentes avec l’information, et qu’une batterie commune peut séparer ces modes :
S2 efface — toute condition initiale relaxe vers l’un des deux attracteurs ponctuels ; l’information de l’état initial est dissipée dans le choix du bassin (seul survit le bit de bassin — c’est l’hystérésis de l’exercice 3) ;
S5 régénère — le motif spatial s’auto-entretient et se répare après ablation (la morphogenèse recolle le trou) ;
GOL transporte — le glider déplace une forme intacte à vitesse c/4, sans la régénérer ni la dissiper.
Trois axes de mesure, chacun exécuté à l’identique sur les trois substrats :
Axe T — transport : déplacement du centre de masse de la structure localisée + signature de population ;
Axe R — réparation : intervention do(ablation) (Pearl), puis recovery_score / time_to_recover (ict.agency) ;
Axe S — enjeu : indice I_stake (ict.stake) — retour actif vers le bassin après un kick, au-delà de la dérive libre.
Chaque axe produit une signature, pas un score : la conclusion attendue est un profil à trois colonnes, pas un classement.
0. Les prémisses vivantes — certificats avant mesures
Chaque mesure ne vaut que si le substrat est dans son régime documenté. Trois certificats, exécutés ici (pas affirmés) :
GOL : calibrate_all() — les cinq patterns canoniques doivent reproduire leurs constantes (périodes, déplacements) dans le moteur B3/S23 ;
S2 : equilibria(c) — à c = 2.2, le paturage de May doit montrer ses deux attracteurs stables séparés par un instable (bistabilité) ;
S5 : le régime par défaut de GrayScott (F = 0.0367, k = 0.0649) est le régime taches mitotiques de Pearson — la croissance de la masse V ci-dessous en sera la preuve dynamique.
cert = calibrate_all()print("Certificat GOL (ict.life.calibrate_all) :", cert)print("5/5 :", all(cert.values()))C_BISTABLE =2.2graze = GrazingModel(r=1.0, K=10.0, h=1.0)eqs = graze.equilibria(C_BISTABLE)print(f"\nCertificat S2 (paturage de May, c={C_BISTABLE}) : equilibres (x*, stable)")for x, st in eqs:print(f" x* = {x:6.3f}{'stable'if st else'instable'}")x_haut =max(x for x, st in eqs if st)x_bas =min(x for x, st in eqs if st and x >1e-9)gs = GrayScott()print(f"\nCertificat S5 (regime declare) : F={gs.F}, k={gs.k}, Du={gs.Du}, Dv={gs.Dv}")print("(regime taches mitotiques de Pearson, Science 1993 — stable pour dt=1.0)")
calibrate_all() rend 5/5 True : le moteur B3/S23 reproduit les constantes canoniques (blinker p=2, pulsar p=3, glider p=4 diagonal, LWSS p=4 c/2, block p=1) — les mesures GOL de ce notebook héritent de ce certificat, lui-même adossé au théorème Lean hashlife_correct (cf ICT-Life, section 5).
À c = 2.2, le paturage montre bien deux équilibres stables (un haut végétalisé et un bas surpâture) séparés par un instable : c’est le paysage à deux bassins que l’axe S fera basculer.
Gray-Scott démarre en régime mitotique — la preuve dynamique arrive avec l’axe T (la masse V va croître par division des taches).
1. Axe T — transport d’information localisée
Protocole commun : faire évoluer chaque substrat depuis son état canonique pendant un horizon fixe, puis mesurer
le déplacement du centre de masse (COM) de la structure localisée — pour GOL : cellules vivantes ; pour S5 : champ V seuillé à 0.2 ; pour S2 : structurellement indéfini (0-D, aucune coordonnée à déplacer — mesuré et rapporté comme tel, pas caché) ;
la signature de population — population vivante (GOL), masse du champ V (S5), biomasse x (S2).
Horizons : 40 générations (GOL), 1200 pas (S5), 400 pas d’Euler dt=0.05 (S2) — chacun dans son régime.
def com_cells(grid):# Centre de masse des cellules vivantes (substrat GOL). cs = live_cells(grid) ys = np.array([c[0] for c in cs], dtype=float) xs = np.array([c[1] for c in cs], dtype=float)returnfloat(ys.mean()), float(xs.mean())def com_field(field, thr):# Centre de masse du champ seuille (substrat S5). m = field > thrifnot m.any():returnfloat("nan"), float("nan") iy, ix = np.nonzero(m) w = field[m]returnfloat((iy * w).sum() / w.sum()), float((ix * w).sum() / w.sum())# --- GOL : glider depuis la position canonique ---H_GOL =40g0 = embed(canonical_pattern("glider"), size=32, top=2, left=2)gol_traj = trajectory(g0, H_GOL)gol_pop = np.array([int(t.sum()) for t in gol_traj])gol_com = np.array([com_cells(t) for t in gol_traj])gol_disp = gol_com[-1] - gol_com[0]print(f"GOL : population = {sorted(set(gol_pop.tolist()))} (constante), "f"deplacement COM @ {H_GOL} generations = ({gol_disp[0]:.1f}, {gol_disp[1]:.1f})"f" -> vitesse diag {(gol_disp[0]/H_GOL):.2f} = c/4")# --- S5 : germe central, croissance mitotique ---H_S5, REC_S5 =1200, 40rng5 = np.random.default_rng(31)U0, V0 = gs.seed(n=64, block=8, noise=0.02, rng=rng5)U_T, V_T, snaps5 = gs.run(U0, V0, H_S5, record_every=REC_S5, include_initial=True)s5_mass = np.array([float(s[s >0.2].sum()) for s in snaps5])s5_com = np.array([com_field(s, 0.2) for s in snaps5])s5_disp = np.linalg.norm(s5_com[-1] - s5_com[0])print(f"S5 : masse V>0.2 {s5_mass[0]:.1f} -> {s5_mass[-1]:.1f} (croissance par division : replication), "f"|deplacement COM| = {s5_disp:.2f} cellules (~immobile)")print(f" cellules V>0.3 a t={H_S5} : {int((snaps5[-1] >0.3).sum())}")# --- S2 : relaxation vers l'attracteur haut ---H_S2, DT_S2 =400, 0.05x =8.0xs_t = [x]for _ inrange(H_S2): x =max(0.0, x + DT_S2 *float(graze.rate(x, C_BISTABLE))) xs_t.append(x)xs_t = np.array(xs_t)print(f"S2 : x(t) {xs_t[0]:.2f} -> {xs_t[-1]:.3f} (attracteur haut x* = {x_haut:.3f}), "f"COM : n/a (0-D, aucune coordonnee)")print(f" Le 'transport' du bistable est un choix de bassin, pas un deplacement : "f"l'information d'etat est effacee au profit d'un bit.")
GOL : population = [5] (constante), deplacement COM @ 40 generations = (10.0, 10.0) -> vitesse diag 0.25 = c/4
S5 : masse V>0.2 15.7 -> 39.2 (croissance par division : replication), |deplacement COM| = 0.26 cellules (~immobile)
cellules V>0.3 a t=1200 : 61
S2 : x(t) 8.00 -> 6.860 (attracteur haut x* = 6.860), COM : n/a (0-D, aucune coordonnee)
Le 'transport' du bistable est un choix de bassin, pas un deplacement : l'information d'etat est effacee au profit d'un bit.
# Signatures de population et de transport, cote a cote.fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 3.6))t_gol = np.arange(len(gol_traj))d_gol = np.linalg.norm(gol_com - gol_com[0], axis=1)t_s5 = np.arange(len(snaps5)) * REC_S5d_s5 = np.linalg.norm(s5_com - s5_com[0], axis=1)ax1.plot(t_gol, d_gol, "o-", ms=3, label="GOL (glider, cellules vivantes)")ax1.plot(t_s5, d_s5, "s-", ms=3, label="S5 (Gray-Scott, champ V > 0.2)")ax1.set_xlabel("temps (pas du substrat)")ax1.set_ylabel("|deplacement COM| (cellules)")ax1.set_title("Transport : seul le glider s'eloigne de son depart\n(S2 : n/a — 0-D)")ax1.legend(fontsize=8)ax2.plot(t_gol / H_GOL, gol_pop / gol_pop[0], "o-", ms=3, label="GOL : population / population(0)")ax2.plot(t_s5 / H_S5, s5_mass / s5_mass[0], "s-", ms=3, label="S5 : masse V / masse(0)")ax2.plot(np.arange(len(xs_t)) / H_S2, xs_t / xs_t[0], "^-", ms=3, label="S2 : x / x(0)")ax2.set_xlabel("temps normalise t / T")ax2.set_ylabel("population normalisee")ax2.set_title("Signatures : GOL constant (transport),\nS5 croissant (replication), S2 relaxant (effacement)")ax2.legend(fontsize=8)plt.tight_layout()plt.show()
Lecture — trois signatures, un seul transporteur
GOL : population exactement constante (5 cellules, sorted(set(...)) = [5]) pendant que le COM parcourt (10.0, 10.0) en 40 générations — soit (1,1) toutes les 4 générations, la vitesse diagonale canonique c/4. La forme est déplacée intacte : c’est la définition d’un transport d’information localisée.
S5 : la masse V passe de 15.7 à 39.2 (≈ ×2.5) — l’information est répliquée (les taches se divisent), pas transportée : le COM reste à ~0 cellule du départ. Chaque tache reste sur place ; ce qui voyage, c’est la frontière de colonisation.
S2 : x(t) relaxe de 8.0 vers l’attracteur haut 6.860 — l’état initial est effacé au profit du point fixe. Il n’existe pas de coordonnée à déplacer : le substrat est 0-D.
L’axe T fait donc le travail de séparation attendu : transporter (GOL) ≠ répliquer (S5) ≠ effacer (S2). Mais il ne dit encore rien de la robustesse de ces modes — c’est le rôle de l’axe R.
2. Axe R — réparation après do(ablation) (Pearl)
Protocole commun (celui d’ICT-11, étendu aux trois substrats) : abîmer la structure localisée par une intervention explicite do(·), laisser évoluer, mesurer la fraction de structure restaurée :
S5 : ict.agency.ablate — réinitialiser un disque de rayon 6 autour d’une tache à l’état nu (U=1, V=0), puis recovery_score (variance restaurée dans le masque) et time_to_recover ;
GOL : ablation de la cellule de tête du glider (la cellule vivante la plus « en arrière » du sens de marche), puis détection de pattern (period_and_displacement : le glider ne survit que si la trajectoire redevient 4-périodique de déplacement (1,1)) — plus honnête qu’un simple comptage de cellules, car 6 cellules de débris ne sont pas un glider ;
S2 : l’ablation 0-D est un kick — x ← 0.65 · x*, puis temps de retour à 1% de l’attracteur.
# --- S5 : ablation d'une tache, mesure de la regeneration ---iy, ix = np.unravel_index(np.argmax(V_T), V_T.shape)mask_R = disk_mask(64, float(ix), float(iy), 6.0)s_ref5 =float(np.var(V_T[mask_R]))U_abl5, V_abl5 = ablate(U_T, V_T, mask_R)U_rep5, V_rep5, snaps_R5 = gs.run(U_abl5, V_abl5, 360, record_every=20, include_initial=True)recovery5 = recovery_score(V_T, V_abl5, snaps_R5[-1], mask_R)struct_traj5 = [float(np.var(s[mask_R])) for s in snaps_R5]ttr5 = time_to_recover(struct_traj5, target=s_ref5, tol=0.15, record_every=20)print(f"S5 : ablation disque r=6 sur la tache (argmax V)")print(f" variance reference = {s_ref5:.4f} -> reparee = {struct_traj5[-1]:.4f}")print(f" recovery_score = {recovery5:.3f} time_to_recover (85% ref) = {ttr5} pas")fig, axes = plt.subplots(1, 3, figsize=(10, 3.4))for ax, field, title inzip( axes, (V_T, V_abl5, snaps_R5[-1]), ("reference (t=1200)", "juste apres do(ablation)", "apres 360 pas (regeneration)"),): ax.imshow(field, cmap="inferno", vmin=0.0, vmax=0.5) ax.add_patch(plt.Circle((ix, iy), 6.0, fill=False, color="cyan", lw=1.2)) ax.set_title(title, fontsize=9) ax.set_xticks([]); ax.set_yticks([])plt.suptitle("S5 Gray-Scott : la tache ablatee se recolle partiellement (do-calculus d'ICT-11)", fontsize=10)plt.tight_layout()plt.show()
S5 : ablation disque r=6 sur la tache (argmax V)
variance reference = 0.0155 -> reparee = 0.0038
recovery_score = 0.244 time_to_recover (85% ref) = None pas
Lecture chiffrée — la variance comme jauge de réparation. La métrique de la cellule n’est pas un compteur de cellules vivantes mais la variance du champ \(V\) dans le disque\(r=6\) centré sur l’argmax : la référence vaut \(0{,}0155\), et après 360 pas de régénération la variance mesurée est retombée à \(0{,}0038\) — le champ se recolle à ~25 % de sa variance d’origine (\(0{,}0038/0{,}0155 \approx 0{,}245\)). L’échec de time_to_recover se lit dans le même compteur, sans quitter la sortie : la cible était 85 % de la référence, soit \(0{,}85 \times 0{,}0155 \approx 0{,}0132\), et la variance réparée \(0{,}0038\) n’en représente qu’environ 29 % — l’horizon 360 pas ne peut pas l’atteindre. La figure décline le constat en trois panneaux (référence \(t=1200\), juste après do(ablation), après 360 pas) : le cercle cyan de rayon 6 cerne la zone dévastée, et le panneau final montre le bord du disque recolonisé par les taches voisines — c’est la mitose qui régénère le site, pas le site lui-même.
# --- GOL : ablation de la cellule arriere du glider, detection de pattern ---cells_g =sorted(live_cells(g0), key=lambda c: c[0] + c[1])arriere = cells_g[0]site_g = disk_mask(32, float(arriere[1]), float(arriere[0]), 2.5)g_abl = g0.copy()g_abl[arriere[0], arriere[1]] =Falsetraj_abl = trajectory(g_abl, 40)p_abl, d_abl = period_and_displacement(g_abl, max_steps=64)recovery_gol = recovery_score(g0.astype(float), g_abl.astype(float), traj_abl[-1].astype(float), site_g)p_intact, d_intact = period_and_displacement(g0, max_steps=64)print(f"GOL : controle intact -> periode {p_intact}, deplacement {d_intact}")print(f"GOL : ablation de la cellule arriere {arriere}")print(f" periode detectee = {p_abl}, deplacement = {d_abl} -> glider reconstitue : {p_abl ==4and d_abl == (1, 1)}")print(f" population finale = {int(traj_abl[-1].sum())} cellules (debris, pas un glider)")print(f" recovery_score sur le site = {recovery_gol:.3f} (le site ne redevient jamais un glider)")
GOL : controle intact -> periode 4, deplacement (1, 1)
GOL : ablation de la cellule arriere (2, 3)
periode detectee = None, deplacement = None -> glider reconstitue : False
population finale = 6 cellules (debris, pas un glider)
recovery_score sur le site = -1.167 (le site ne redevient jamais un glider)
Lecture chiffrée — une bascule de catégorie, pas une dégradation. La sortie pose d’abord le diagnostic de repos, glider intact : période 4et déplacement (1, 1) — exactement la condition que la cellule va re-tester ensuite. Retirer la cellule arrière \((2, 3)\) suffit : les deux mesures plongent à None et None, et la condition composée (période 4 et déplacement (1, 1)) retombe à False — non pas « moins périodique », mais hors de l’espace des gliders d’un coup. La ligne glider reconstitue : False est un verdict binaire : un seul bit d’état éteint dans le motif fait basculer la catégorie entière, sans état intermédiaire que l’instrument de détection saurait nommer.
# --- S2 : kick 0-D, temps de retour a 1% de l'attracteur ---x_kick =0.65* x_hautx = x_kickt_back =Nonefor t inrange(4000):ifabs(x - x_haut) <=0.01* x_haut and t_back isNone: t_back = t x =max(0.0, x + DT_S2 *float(graze.rate(x, C_BISTABLE)))print(f"S2 : kick x* {x_haut:.3f} -> {x_kick:.3f} (-35%)")print(f" retour a 1% de l'attracteur haut en {t_back} pas Euler (dt = {DT_S2})")print(" -> relaxation, pas regeneration : l'etat revient au MEME point fixe ;")print(" aucune structure n'a ete reconstruite (il n'y en a pas).")
S2 : kick x* 6.860 -> 4.459 (-35%)
retour a 1% de l'attracteur haut en 230 pas Euler (dt = 0.05)
-> relaxation, pas regeneration : l'etat revient au MEME point fixe ;
aucune structure n'a ete reconstruite (il n'y en a pas).
Lecture chiffrée — le retour en 11,5 unités de temps. Le kick part du point fixe haut \(x^* = 6{,}860\) et coupe l’état à \(x = 4{,}459\) — c’est le produit \(0{,}65 \times x^*\) affiché par la sortie comme « −35 % ». Le critère d’arrêt est chiffré dans la boucle : \(\lvert x - x^* \rvert \le 0{,}01\,x^* \approx 0{,}069\), i.e. l’état est « revenu » dès qu’il est à moins de 1 % du point fixe. Il l’atteint en 230 pas Euler (dt = 0,05), soit 11,5 unités de temps simulées — et ce dans une boucle plafonnée à 4 000 pas : le retour consomme 5,75 % de la fenêtre de scrutation. La direction du diagnostic compte autant que la durée : kick vers le bas, retour vers le même point fixe haut — la sortie le dit en toutes lettres : « relaxation, pas regeneration ».
Lecture — régénérative, fragile, relaxative
S5 est régénérative : recovery_score = 0.24 à horizon 360 pas — la tache ablatée recolle partiellement (la mitose des taches voisines re-colonise le disque), sans toutefois restaurer 85% de la variance de référence dans cet horizon (time_to_recover = None). Réparation partielle et lente, mais réelle.
GOL est fragile : retirer une seule cellule au glider détruit le pattern — la trajectoire ne redevient jamais 4-périodique (periode = None), il reste des débris (6 cellules). Le recovery_score du site sort à −1.17 : le site ne se reconstruit pas, il se recouvre de débris non canoniques. Le glider transporte son information ; il ne la défend pas.
S2 est relaxative : kické à −35%, l’état revient à 1% de l’attracteur en 230 pas — mais il n’y a rien à « réparer » : le retour au point fixe est de la dissipation de kick, pas de la reconstruction.
L’axe R inverse donc le portrait de l’axe T : le substrat qui transporte le mieux (GOL) est celui qui répare le moins — son information survit en étant ailleurs (translatée), pas en se restaurant sur place. S5 fait exactement l’inverse. Aucun des deux n’est « plus agent » : ils occupent deux cases différentes du protocole.
3. Axe S — enjeu : retour de bassin après kick (I_stake)
Protocole commun (ict.stake.stake_index) : mesurer la fraction de distance regagnée vers l’ancre du bassin après un kick, moins la même fraction sur une trajectoire libre (soustraction de la dérive spontanée) :
S2 : ancre = moyenne de la trajectoire stochastique libre autour du bassin haut ; kick = do_kick vers le bas ;
S5 : état = champ V (l’U voyage dans une fermeture), ancre = moyenne des derniers champs libres ; kick = l’ablation de l’axe R ;
GOL : un glider translate — une ancre positionnelle serait battue à chaque pas par construction. Choix documenté : représenter l’état recentré sur son COM à chaque pas (invariant translationnel), ancre = moyenne des 4 phases recentrées du glider libre. Le kick reste l’ablation d’une cellule.
# --- S2 : I_stake scalaire ---free_s2 = graze.simulate_sde(C_BISTABLE, x0=x_haut, sigma=0.02, dt=0.05, T=800, seed=7)anchor_s2 = basin_anchor(free_s2)step_s2 =lambda x: max(0.0, x + DT_S2 *float(graze.rate(np.asarray(x, dtype=float), C_BISTABLE)))kicked_s2 = x_haut -0.35* x_hautistake_s2 = stake_index(np.asarray(kicked_s2), step_s2, 400, anchor_s2)print(f"S2 : ancre = {anchor_s2:.3f}, kick -35% -> I_stake = {istake_s2:.3f}")print(" (retour actif au bassin haut : l'enjeu du bistable est un POINT)")
S2 : ancre = 6.837, kick -35% -> I_stake = 0.992
(retour actif au bassin haut : l'enjeu du bistable est un POINT)
# --- S5 : I_stake sur le champ V (U porte par fermeture) ---anchor_s5 = np.mean(np.array(snaps5[-6:]), axis=0) # champ moyen des derniers etats libresholder_k = {"U": U_T.copy()}def step_v_kick(V): U2, V2 = gs.step(holder_k["U"], V) holder_k["U"] = U2return V2holder_f = {"U": U_T.copy()}def step_v_free(V): U2, V2 = gs.step(holder_f["U"], V) holder_f["U"] = U2return V2kicked_s5 = V_T.copy()kicked_s5[mask_R] =0.0# do(ablation) : meme intervention que l'axe Ristake_s5 = stake_index(kicked_s5, step_v_kick, 300, anchor_s5, free_step_fn=step_v_free, free_init=V_T.copy())print(f"S5 : I_stake = {istake_s5:.3f} (kick = ablation du disque, controle libre soustrait)")print(" (retour actif au champ de forme : l'enjeu de Gray-Scott est un MOTIF)")
S5 : I_stake = 1.000 (kick = ablation du disque, controle libre soustrait)
(retour actif au champ de forme : l'enjeu de Gray-Scott est un MOTIF)
# --- GOL : I_stake sur grille recentree (invariant translationnel) ---def recenter(grid): grid = np.asarray(grid).astype(bool) cs = live_cells(grid) out = np.zeros_like(grid)ifnot cs:return out ys = np.array([c[0] for c in cs], dtype=float) xs = np.array([c[1] for c in cs], dtype=float) cy, cx =int(round(ys.mean())), int(round(xs.mean()))for (i, j) in cs: out[(i - cy) %32, (j - cx) %32] =Truereturn outfree_g = trajectory(g0, 60)anchor_g = np.mean([recenter(g).astype(float) for g in free_g[::4]], axis=0)step_g =lambda gr: recenter(next_generation(np.asarray(gr).astype(bool)))# controle : kick nul -> I_stake ~ 0 par constructionistake_gol_intact = stake_index(recenter(g0).astype(float), step_g, 40, anchor_g)g_kick1 = g0.copy()g_kick1[arriere[0], arriere[1]] =Falseistake_gol_1 = stake_index(recenter(g_kick1).astype(float), step_g, 40, anchor_g)g_kick2 = g0.copy()for (i, j) in cells_g[:2]: g_kick2[i, j] =Falseistake_gol_2 = stake_index(recenter(g_kick2).astype(float), step_g, 40, anchor_g)print(f"GOL : controle intact (kick nul) -> I_stake = {istake_gol_intact:.3f} (attendu ~0)")print(f"GOL : kick 1 cellule arriere -> I_stake = {istake_gol_1:.3f}")print(f"GOL : kick 2 cellules arriere -> I_stake = {istake_gol_2:.3f}")print(" (valeurs de part et d'autre de zero : l'instrument ne discrimine pas ici)")
GOL : controle intact (kick nul) -> I_stake = 0.000 (attendu ~0)
GOL : kick 1 cellule arriere -> I_stake = 0.286
GOL : kick 2 cellules arriere -> I_stake = -0.167
(valeurs de part et d'autre de zero : l'instrument ne discrimine pas ici)
Lecture chiffrée — trois décimales pour dire « rien ». L’instrument GOL travaille sur une grille recentrée : le centre de masse du motif est translaté au centre (avec repli modulo 32, sur le tore), et l’ancre est la moyenne de 15 trames (échantillonnage tous les 4 pas d’une trajectoire de 60). Mesures exactes : contrôle intact 0.000, kick d’1 cellule (la cellule arrière de l’axe R) 0.286, kick de 2 cellules −0.167. Deux anomalies à l’œil nu. Pas de dose-réponse : retirer davantage de cellules ne grossit pas la valeur — l’amplitude décroît (0,286 → 0,167) et le signe change. Et pas d’ordre de grandeur commun avec les instruments qui fonctionnent : S2 affiche 0.992, S5 plafonne à 1.000 (sorties exactes, que la table arrondit à 0,99 et 1,00), tandis que le plus grand parasite GOL vaut 0,286 — environ 3,5 fois plus petit que le vrai signal le plus faible (\(0{,}992/0{,}286 \approx 3{,}5\)). La ligne de sortie le résume elle-même : « l’instrument ne discrimine pas ici ».
Lecture — l’instrument bassin est aveugle aux invariants translationnels
S2 : I_stake = 0.99 — retour quasi complet au point. L’instrument mesure exactement ce pour quoi il a été construit : un substrat qui défend un état.
S5 : I_stake = 1.00 (plafonné) — le champ revient vers son motif après ablation, bien au-delà de la dérive libre soustraite. Un substrat qui défend une forme.
GOL : le contrôle intact rend bien I_stake ≈ 0.00, mais les kicks donnent +0.29 (1 cellule) et −0.17 (2 cellules) — des valeurs de part et d’autre de zéro, c’est-à-dire du bruit d’instrument, pas un signal d’enjeu. Et le rappel de l’axe R : dans les deux cas le glider est détruit (période = None).
C’est le point méthodologique du notebook — l’écho exact du Gate 21 d’ICT-25 (le détecteur ne voit pas la persona) : I_stake est un instrument bassin-return ; l’invariant du glider est translationnel (sa « défense » serait de rester une forme qui bouge, pas de revenir à une place). Mesurer l’enjeu d’un substrat translatif avec une ancre de bassin, c’est pointer un thermomètre sur la lumière. La mesure honnête dit : sur l’axe S, GOL n’a pas encore d’instrument — en construire un (par exemple une distance entre distributions de formes plutôt qu’entre états) est un prolongement naturel, pas une conclusion négative.
# --- La table de contraste MESUREE : chaque chiffre vient des cellules ci-dessus ---import pandas as pdtable = pd.DataFrame({"Mesure (protocole commun)": ["Transport : |deplacement COM| / horizon","Population : signature temporelle","Reparation : recovery_score apres do(ablation)","Reparation : survie du pattern (detection 4-periodique)","Enjeu : I_stake (retour de bassin)","Enjeu : verdict d'instrument", ],"S2 bistable (0-D)": ["n/a (aucune coordonnee)",f"x: {xs_t[0]:.1f} -> {xs_t[-1]:.2f} (relaxation au point fixe)",f"relaxation : retour a 1% en {t_back} pas (kick -35%)","n/a (pas de pattern)",f"{istake_s2:.2f}","instrument OK : defend un POINT", ],"S5 Gray-Scott (2-D)": [f"{s5_disp:.2f} cellules / {H_S5} pas (immobile)",f"masse V: {s5_mass[0]:.1f} -> {s5_mass[-1]:.1f} (replication mitotique)",f"{recovery5:.2f} (partielle) ; ttr(85%) = {ttr5}","motif persistant (taches auto-entretenues)",f"{istake_s5:.2f}","instrument OK : defend une FORME", ],"GOL Jeu de la Vie (2-D)": [f"({gol_disp[0]:.0f}, {gol_disp[1]:.0f}) / {H_GOL} generations = c/4",f"population {gol_pop[0]} constante (transport intact)",f"{recovery_gol:.2f} (site recouvert de debris)",f"DETRUIT par l'ablation d'1 cellule (periode = None)",f"{istake_gol_1:+.2f} / {istake_gol_2:+.2f} (bruit d'instrument)","instrument AVEUGLE : invariant translationnel", ],}).set_index("Mesure (protocole commun)")table
S2 bistable (0-D)
S5 Gray-Scott (2-D)
GOL Jeu de la Vie (2-D)
Mesure (protocole commun)
Transport : |deplacement COM| / horizon
n/a (aucune coordonnee)
0.26 cellules / 1200 pas (immobile)
(10, 10) / 40 generations = c/4
Population : signature temporelle
x: 8.0 -> 6.86 (relaxation au point fixe)
masse V: 15.7 -> 39.2 (replication mitotique)
population 5 constante (transport intact)
Reparation : recovery_score apres do(ablation)
relaxation : retour a 1% en 230 pas (kick -35%)
0.24 (partielle) ; ttr(85%) = None
-1.17 (site recouvert de debris)
Reparation : survie du pattern (detection 4-periodique)
n/a (pas de pattern)
motif persistant (taches auto-entretenues)
DETRUIT par l'ablation d'1 cellule (periode = ...
Enjeu : I_stake (retour de bassin)
0.99
1.00
+0.29 / -0.17 (bruit d'instrument)
Enjeu : verdict d'instrument
instrument OK : defend un POINT
instrument OK : defend une FORME
instrument AVEUGLE : invariant translationnel
Lecture — un profil, pas un classement
La table se lit en colonnes, et chaque colonne est un mode de relation à l’information :
S2
S5
GOL
Mode
effacer
régénérer
transporter
L’information survit…
comme un bit (bassin choisi)
en se recollant sur place
en étant ailleurs
Faiblesse mesurée
aucun espace
immobile
fragile à l’impact direct
Instrument qui la voit
I_stake ✓
I_stake + recovery ✓
period/displacement ✓ — I_stake ✗
Aucun axe ne dit « GOL gagne » : l’axe T le distingue (seul transporteur), l’axe R le distingue en défaveur (seul non-réparateur), l’axe S ne le voit pas. C’est exactement ce qu’un banc discriminant doit produire : une signature par substrat, et la connaissance précise de ce que chaque instrument ne voit pas — condition pour ne pas construire la suite (émergence causale, tranche en cours sur les mêmes trajectoires) sur un aveugle.
Exercice 1 — balayage d’ablation du glider : position et taille
La cellule de l’axe R n’a testé que la cellule « arrière ». Balayez toutes les cellules du glider × toutes les tailles d’ablation (1, 2, 3 cellules) : pour chaque configuration, déterminez si la trajectoire redonne un glider (période 4, déplacement (1,1)).
Question : le taux de survie dépend-il de la position des cellules retirées (tête vs queue vs flanc) ? Une ablation de 2 cellules « symétriques » survit-elle mieux que 2 cellules adjacentes ?
Indice : sorted(live_cells(g0), key=...) donne les candidats ; itertools.combinations énumère les ensembles ; period_and_displacement(g, max_steps=64) rend le verdict.
# Etape 1 : enumerer toutes les combinaisons de 1, 2 et 3 cellules du glider initial.# Etape 2 : pour chaque combinaison, ablater puis evoluer et tester (periode=4, deplacement=(1,1)).# Etape 3 : agregger par taille -> taux de survie ; par position -> effet tete/queue.def taux_survie_ablation(horizon=40): result =None# TODO etudiant : dict {taille: taux_survie} + detail par positionreturn resultprint("Exercice a completer : taux_survie_ablation")
Exercice a completer : taux_survie_ablation
Exercice 2 — dose-réponse du Gray-Scott : le paramètre feed F
Le régime mitotique (F = 0.0367) n’est qu’un point de l’espace (F, k). Pour une petite grille de valeurs de F (par ex. 0.030, 0.0367, 0.045, 0.055, à k fixe), faites pousser le substrat avec le protocole de l’axe T, comptez les taches formées à horizon fixe, puis mesurez le recovery_score après ablation d’une tache (protocole de l’axe R).
Question : la capacité de régénération a-t-elle un seuil en F ? Le régime qui régénère le mieux est-il celui qui a le plus de taches ?
Indice : compter les taches = composantes connexes du champ seuillé V > 0.3 (un parcours en profondeur ou en largeur suffit sur une grille 64×64) ; réutilisez disk_mask + recovery_score telles quelles.
# Etape 1 : pour chaque F de la grille, seed + run (memes parametres que l'axe T) a horizon fixe.# Etape 2 : compter les composantes connexes de V > 0.3 (les taches).# Etape 3 : ablater la tache la plus brillante et mesurer recovery_score (protocole axe R).def dose_reponse_F(F_grid=(0.030, 0.0367, 0.045, 0.055), steps=1200, rayon=6.0): result =None# TODO etudiant : liste de dicts {F, nb_taches, recovery_score}return resultprint("Exercice a completer : dose_reponse_F")
Exercice a completer : dose_reponse_F
Exercice 3 — hystérésis du bistable : la mémoire de chemin comme « transport » 0-D
L’axe T a mesuré que S2 ne transporte rien dans l’espace — mais le bistable transporte quelque chose le long du paramètre : faites monterc de 1.5 à 3.5 puis redescendre de 3.5 à 1.5 (rampe lente, simulate_ramp), en partant chaque fois du bassin haut.
Question : le seuil de bascule à la montée (c↑) et celui à la descente (c↓) coïncident-ils ? L’écart entre les deux est l’hystérésis — la trace laissée par l’histoire du trajet, seul « transport d’information » d’un substrat sans espace.
Indice : détectez la bascule quand x passe sous 2.0 ; comparez le c de bascule montée vs descente ; un sigma = 0.0 rend l’expérience déterministe.
# Etape 1 : rampe montee c: 1.5 -> 3.5 (simulate_ramp, x0 = bassin haut, sigma = 0).# Etape 2 : rampe descente c: 3.5 -> 1.5 en repartant du bassin haut.# Etape 3 : detecter les deux seuils (x < 2.0) et rapporter l'ecart = hysteresis.def seuils_hysteresis(c_lo=1.5, c_hi=3.5, n=6000): result =None# TODO etudiant : dict {"c_montee": ..., "c_descente": ..., "ecart": ...}return resultprint("Exercice a completer : seuils_hysteresis")
Exercice a completer : seuils_hysteresis
Conclusion — ce que le contraste mesure, et ce qu’il laisse ouvert
Ce notebook a exécuté le même protocole sur les trois substrats de la batterie ICT, et la table mesurée établit le profil annoncé par le dispatch #5726 :
GOL est le seul transporteur — population constante, COM à c/4, forme intacte (axe T) ;
GOL est le seul non-réparateur — une cellule retirée détruit le glider, sans régénération ; S5 recolle partiellement, S2 relaxe (axe R) ;
L’instrument d’enjeu I_stake voit S2 et S5, pas GOL — l’invariant du glider est translationnel ; la mesure honnête de cet axe est la déclaration de cet aveugle (axe S).
Ce que ce notebook ne fait pas : l’émergence causale (TPM coarse-grainée, information effective) sur ces trajectoires — c’est l’objet de la tranche en cours sur les trajectoires certifiées (issue #5726), à laquelle ce contraste fournit le socle des trois régimes. Les mono-substrats se creusent dans leurs notebooks respectifs : ICT-8 (paysages et early-warning du bistable), ICT-9 (morphogenèse Gray-Scott), ICT-Life (calibration GOL + pont Lean hashlife_correct).
Suite naturelle : un instrument d’enjeu translationnel (distance entre distributions de formes), et le croisement de cette table avec l’information effective des trajectoires certifiées.