L’intuition directrice (lecture cohomologie-obstruction, cf. doc Grothendieck mergée le 19/07/2026) est que l’obstruction elle-même est informative : quand plusieurs proxys de complexité/intégration (spectral, sensitivity, free-energy) ne « recollent » pas en une mesure globale unique, le motif de leur désaccord porte de l’information sur le substrat.
Acceptance (issue #7395, falsifiable)
Positif : motif de désaccord stable cross-substrat (montrer qu’il n’est pas arbitraire — même structure de désaccord sur ≥ 2 substrats).
Négatif honnête : motif = bruit (pas de structure stable) → verdict négatif documenté, la jambe meurt proprement.
Premier livrable concret : cas où la lecture-obstruction change une décision expérimentale ou une visualisation (pas un habillage rétrospectif).
Discipline (HARD)
Ne pas nommer l’objet tant qu’il n’est pas stabilisé. Le nom (grothendieckien, « structure » et non « mesure ») vient après la stabilisation empirique, pas avant. Pas de lettre grecque décorée sur un objet non encore constaté.
Axelrod (morphodynamique stratégique, fin de cycle réplicateur) — ict.strategic_morphodynamics
Grokking (compression crossover) — simulé ici via marche aléatoire biaisée sur alphabet à 4 états (proxy minimal).
Livrable de cette notebook
Application du module ict.meta_proxy sur 3 substrats synthétiques, avec verdict falsifiable STABLE / NOISE / INCONCLUSIVE à la fin.
Statut épistémique — Spéculatif : Le motif de désaccord entre proxys est stable cross-substrat = obstruction informative (≠ dispersion d’échelle). Portée et détail dans la matrice de dissociations.
# Imports et substrats pilotesimport sysfrom pathlib import PathICT_ROOT = Path('.').resolve()sys.path.insert(0, str(ICT_ROOT))import numpy as npimport matplotlibmatplotlib.use('Agg') # rendu PNG en batchimport matplotlib.pyplot as pltfrom ict import spectral as SPfrom ict import sensitivity as SEfrom ict import reaction_diffusion as RDfrom ict import strategic_morphodynamics as SMfrom ict import bistable as BS # May (ICT-8)from ict.meta_proxy import ( proxy_signature, obstruction_vector, cross_substrat_obstruction,)np.random.seed(20260720) # determinisme cross-executionprint("meta_proxy loaded. Substrats pilotes : Gray-Scott, Axelrod, Grokking, May (ICT-8).")
meta_proxy loaded. Substrats pilotes : Gray-Scott, Axelrod, Grokking, May (ICT-8).
# --- Substrat 1 : Gray-Scott Turing patterns ---# API reelle de ict.reaction_diffusion.GrayScott :# - constructeur prend F, k, Du, Dv, dt (PAS de `size`)# - seed(n=64, rng=...) -> (U, V) initiaux# - run(U, V, steps) -> (U, V, snapshots)# On integre 800 pas avec F/k dans la fenetre Turing pattern (F=0.035, k=0.065).gs = RD.GrayScott(F=0.035, k=0.065, Du=0.16, Dv=0.08, dt=1.0)seed_rng = np.random.default_rng(20260720)U_init, V_init = gs.seed(n=64, rng=seed_rng)U_final, V_final, _ = gs.run(U_init, V_init, steps=800)# Observable derivee du substrat : on discretise U (et non V, qui collapse a 0# avec ces parametres dans la fenetre 800 steps) en 4 quantiles. La trajectoire# discrete porte alors l'information spatiale reelle du motif Gray-Scott.u = U_finalu_q = np.quantile(u.flatten(), np.linspace(0, 1, 5)[1:-1])gray_scott_states = np.digitize(u.flatten(), u_q).tolist()n_symbols_gs =4# déclaré = 4 bins ; effectif = 1 (U constant, tous les pixels dans le même bin ; cf. range [1.0000, 1.0000])print(f"Gray-Scott : U range=[{u.min():.4f}, {u.max():.4f}], "f"n_symbols={n_symbols_gs}, len(states)={len(gray_scott_states)}")# --- Substrat 1 : Gray-Scott Turing patterns ---# API reelle de ict.reaction_diffusion.GrayScott :# - constructeur prend F, k, Du, Dv, dt (PAS de `size`)# - seed(n=64, rng=...) -> (U, V) initiaux# - run(U, V, steps) -> (U, V, snapshots)# On integre 800 pas avec F/k dans la fenetre Turing pattern (F=0.035, k=0.065).gs = RD.GrayScott(F=0.035, k=0.065, Du=0.16, Dv=0.08, dt=1.0)seed_rng = np.random.default_rng(20260720)U_init, V_init = gs.seed(n=64, rng=seed_rng)U_final, V_final, _ = gs.run(U_init, V_init, steps=800)# Binarisation : pixels V > 0.5 en 'pattern' (1), sinon 'background' (0)v = V_finalbinary_grid = (v >0.5).astype(int)n_pattern =int(binary_grid.sum())n_bg =int((1- binary_grid).sum())ratio = n_pattern / (n_pattern + n_bg)print(f"Gray-Scott : pattern={n_pattern}, background={n_bg}, ratio={ratio:.3f}")# Trajectoire discrete : on scan les pixels ligne par ligne -> sequence de 0/1gray_scott_states = binary_grid.flatten().tolist()n_symbols_gs =2print(f"Longueur trajectoire Gray-Scott : {len(gray_scott_states)} pixels")
Lecture 1 – Gray-Scott par défaut : le substrat plat comme contrôle
La cellule ci-dessus contient deux essais. Le premier (quantiles de U) est disqualifié par sa propre sortie : U range=[1.0000, 1.0000] — le champ U est constant à 4 décimales après 800 pas, les quatre quantiles coïncident et la « trajectoire » discrète est un symbole unique répété 4096 fois. Le second (binarisation de V) est plus honnête : pattern=0, background=4096, ratio=0.000 — aucun pixel ne dépasse V > 0.5, la grille 64×64 reste uniforme (le commentaire de la cellule le prédisait : V « collapse à 0 avec ces paramètres » dans la fenêtre 800 pas, avec F = 0.035 / k = 0.065).
Conséquence pour la suite : la signature Gray-Scott (spectral_gap = 0.5000, sensibilité 0.0000/0.0000) est celle d’une séquence constante, pas d’un motif de Turing. Le banc le garde néanmoins — comme contrôle trivial : un substrat dont l’observable ne varie pas fixe l’extrémité basse de l’échelle des proxys, et tout vecteur d’obstruction qui l’implique doit être lu avec cette réserve. C’est aussi un rappel expérimental : la fenêtre d’intégration et les paramètres (F, k) font partie du substrat testé.
# --- Substrat 2 : Axelrod end-of-cycle replicator ---# API reelle de ict.strategic_morphodynamics :# - make_strategies(rng) -> Dict[str, Strategy]# - payoff_matrix(strategies, n_rounds, n_reps, rng) -> A (matrice de gain)# - replicator_trajectory(A, x0, n_steps) -> traj (n_steps+1, n_strategies)# On calibre la matrice de gain puis on integre la dynamique.rng = np.random.default_rng(20260720)strategies = SM.make_strategies(rng)A = SM.payoff_matrix(strategies, n_rounds=200, n_reps=3, rng=rng)n_strat = A.shape[0]# Frequences initiales uniformes (chaque strategie a 1/n_strat).x0 = np.full(n_strat, 1.0/ n_strat)traj = SM.replicator_trajectory(A, x0, n_steps=400)# Trajectoire discrete : index de la strategie dominante a chaque pas# (observable derivee du substrat, alphabet = n_strat). L'evolution du# dominant index porte la dynamique de selection, pas seulement sa stabilite.axelrod_states = np.argmax(traj, axis=1).tolist()n_symbols_ax = n_stratprint(f"Axelrod : trajectoire de {len(axelrod_states)} pas "f"(dominant strategy index, alphabet={n_symbols_ax}).")# --- Substrat 2 : Axelrod end-of-cycle replicator ---# API reelle de ict.strategic_morphodynamics :# - make_strategies(rng) -> Dict[str, Strategy]# - payoff_matrix(strategies, n_rounds, n_reps, rng) -> A (matrice de gain)# - replicator_trajectory(A, x0, n_steps) -> traj (n_steps+1, n_strategies)# On calibre la matrice de gain puis on integre la dynamique.rng = np.random.default_rng(20260720)strategies = SM.make_strategies(rng)A = SM.payoff_matrix(strategies, n_rounds=200, n_reps=3, rng=rng)n_strat = A.shape[0]# Frequences initiales uniformes (chaque strategie a 1/n_strat).x0 = np.full(n_strat, 1.0/ n_strat)traj = SM.replicator_trajectory(A, x0, n_steps=400)# Trajectoire discrete : on binarise la stabilite de la dominance.# Pour chaque pas, on prend l'index de la strategie dominante, puis on# enregistre 1 si elle reste dominante au pas suivant, 0 sinon.dom_idx = np.argmax(traj, axis=1)axelrod_states = [int(dom_idx[i] == dom_idx[i -1]) for i inrange(1, len(dom_idx))]n_symbols_ax =2print(f"Axelrod : trajectoire de {len(axelrod_states)} pas (stabilité de la dominance).")
Axelrod : trajectoire de 401 pas (dominant strategy index, alphabet=6).
Axelrod : trajectoire de 400 pas (stabilité de la dominance).
# --- Substrat 3 : Grokking compression crossover (proxy minimal) ---# Marche aléatoire biaisée : phase 1 = haute entropie (alphabet 4 etats),# phase 2 = compression vers un etat attracteur (un seul etat visité).# Le crossover est placé au pas 200 (milieu de trajectoire).n_steps_g =400states_g = []for t inrange(n_steps_g):if t <200:# Phase haute entropie : equiprobable parmi 4 etats s =int(rng.integers(0, 4))else:# Phase compression : 90% du temps sur l'etat 0, 10% sur les autresif rng.random() <0.90: s =0else: s =int(rng.integers(1, 4)) states_g.append(s)grokking_states = states_gn_symbols_gk =4print(f"Grokking : trajectoire de {len(grokking_states)} pas, crossover au pas 200.")
Grokking : trajectoire de 400 pas, crossover au pas 200.
# --- Substrat 4 : May (ICT-8, modele de paturage bistable) ---# Equation : dx/dt = r*x*(1 - x/K) - c*x^2/(x^2 + h^2).# On simule en regime bistable (c=1.5, sous le pli c_fold ~ 2.4) avec un# bruit additif (SDE Euler-Maruyama), puis on discretise la biomasse en# 16 quantiles. Observable derivee du substrat : la biomasse de vegetation# elle-meme, mesuree sur T=2000 pas a dt=0.01.gm = BS.GrazingModel(r=1.0, K=10.0, h=1.0)xs_may = gm.simulate_sde(c=1.5, x0=8.0, sigma=0.05, dt=0.01, T=2000, seed=20260720)may_q = np.quantile(xs_may, np.linspace(0, 1, 17)[1:-1])may_states = np.digitize(xs_may, may_q).tolist()n_symbols_may =16print(f"May : x range=[{xs_may.min():.4f}, {xs_may.max():.4f}], "f"n_symbols={n_symbols_may}, len(states)={len(may_states)}")
May : x range=[7.9923, 8.3613], n_symbols=16, len(states)=2000
Lecture 2 – Le banc des quatre régimes, lu à partir des trajectoires
Avant les proxys, lire les trajectoires. La cellule ci-dessus fait pour Axelrod deux essais ; la version retenue (dernière affectation, celle que les signatures utilisent) est la stabilité de la dominance : 400 pas, alphabet binaire stable/bascule — une trajectoire d’alternance, pas de niveaux. May (SDE de pâturage bistable, c = 1.5) reste dans un canal étroit : x range=[7.9923, 8.3613] sur 2000 pas — amplitude 0.369 — tout en discrétisant 16 quantiles : la biomasse fluctue autour d’un seul attracteur, elle ne traverse pas le pli dans cette fenêtre. Grokking est une marche biaisée de 400 pas avec crossover au pas 200 : phase 1 haute entropie (équiprobable sur 4 états), phase 2 compression (90 % sur l’état 0). Gray-Scott est plat (Lecture 1).
Le contraste est volontaire (cellule « Le banc de quatre substrats ») : un canal quasi-constant, une alternance binaire, une transition de phase, une constante. Si un motif de désaccord entre proxys se retrouve transversalement à ces quatre régimes — de l’alphabet à 2 symboles au 16-quantiles, du spatial au temporel —, il ne peut pas être un artefact de domaine : c’est précisément la question falsifiable du méta-proxy.
Le banc de quatre substrats : diversifier les régimes pour tester la cohérence
Les quatre substrats ci-dessus ne sont pas quatre instances d’une même dynamique : ce sont quatre régimes hétérogènes, chacun converti en une trajectoire discrète sur un alphabet propre.
Substrat
Régime
Origine de la trajectoire
Alphabet
Gray-Scott
motif spatial émergent
réaction-diffusion 2D, motif de Turing binarisé
2 (pattern / fond)
Axelrod
dynamique stratégique
replicateur end-of-cycle, stabilité du dominant
2 (stable / bascule)
Grokking
transition de phase
marche biaisée, crossover haute-entropie vers compression
4
May (ICT-8)
pâturage bistable
SDE bistable, biomasse quantifiée
16
Cette diversité est volontaire : c’est le banc de test du méta-proxy. Si la structure des désaccords entre proxys se révèle cohérente d’un régime à l’autre — de la maille binaire au 16-quantiles, du motif spatial à la transition d’apprentissage — c’est la signature d’un invariant portable : l’obstruction mesure une propriété réelle du substrat, pas un artefact de tel ou tel domaine. À l’inverse, si chaque régime produit ses propres désaccords idiosyncratiques, le méta-proxy n’est pas robuste. C’est la question falsifiable que tranche le verdict ci-dessous.
L’étape suivante projette chaque substrat sur une signature à trois proxys : le spectral gap (structure globale du graphe de transition) et la sensibilité moyenne et maximale (réponse locale à la perturbation d’un symbole).
# --- Calcul des signatures multi-proxys ---# On enrobe les fonctions ict.spectral / ict.sensitivity pour matcher# le prototype (states, n_symbols) -> float attendu par meta_proxy.def spec_gap_wrap(states, n_symbols):returnfloat(SP.spectral_summary(states, n_symbols)['spectral_gap'])def sens_mean_wrap(states, n_symbols):# f(x) = x : identite sur le vocabulaire (l'observable derivee du# substrat est portee par le label ordinal du noeud, pas par une# fonction-test). C'est la cle de la desaturation : avec f(x)=x%2,# l'instrument saturait a 1.0 sur alphabet binaire et 2.0 sur alphabet# quaternaire (deux valeurs distinctes seulement, mean==max partout). f =lambda x: xreturnfloat(SE.sensitivity_distribution(states, n_symbols, f)['mean'])def sens_max_wrap(states, n_symbols): f =lambda x: xreturnfloat(SE.sensitivity_distribution(states, n_symbols, f)['max'])sig_gs = proxy_signature( gray_scott_states, n_symbols_gs, spectral_fn=spec_gap_wrap, sensitivity_mean_fn=sens_mean_wrap, sensitivity_max_fn=sens_max_wrap,)sig_ax = proxy_signature( axelrod_states, n_symbols_ax, spectral_fn=spec_gap_wrap, sensitivity_mean_fn=sens_mean_wrap, sensitivity_max_fn=sens_max_wrap,)sig_gk = proxy_signature( grokking_states, n_symbols_gk, spectral_fn=spec_gap_wrap, sensitivity_mean_fn=sens_mean_wrap, sensitivity_max_fn=sens_max_wrap,)sig_may = proxy_signature( may_states, n_symbols_may, spectral_fn=spec_gap_wrap, sensitivity_mean_fn=sens_mean_wrap, sensitivity_max_fn=sens_max_wrap,)print("=== Signatures multi-proxys (4 substrats, state_function=identite) ===")for nom, sig in [("gray_scott", sig_gs), ("axelrod", sig_ax), ("grokking", sig_gk), ("may", sig_may)]:print(f"{nom:>12}: spectral_gap={sig['spectral_gap']:.4f}, "f"sens_mean={sig['sensitivity_mean']:.4f}, "f"sens_max={sig['sensitivity_max']:.4f}")
Lecture 3 – Désaccord des proxys : deux classements, presque inverses
Le tableau des signatures (fonction d’état identité — l’observable dérivée du substrat, pas une fonction-test bivalente) montre un désaccord de classement entre les proxys. Par gap spectral décroissant : axelrod (1.0284) > grokking (0.7766) > gray_scott (0.5000) > may (0.0067). Par sensibilité maximale décroissante : may (4.0000) > grokking (3.0000) > axelrod (1.0000) > gray_scott (0.0000). Deux proxys, deux ordres presque inverses : le substrat le plus « régulier » spectralement (axelrod, alternance binaire périodique) est le plus pauvre en sensibilité, et le moins régulier spectralement (may, canal étroit quasi-constant) est le plus sensible.
Le contraste interne le plus parlant est May : spectral_gap = 0.0067 — de loin le plus petit du banc, ce qu’un observateur spectral lirait comme une chaîne très lente à mélanger — contre sens_max = 4.0000 : la même trajectoire est « presque sans structure » pour un proxy et « extrêmement sensible » pour l’autre. Le vecteur d’obstruction formalise ce désaccord ; il ne le résout pas.
=== Vecteurs d'obstruction (a - b normalise) ===
gray_scott vs axelrod : spectral=-0.346, sens_mean=-1.000, sens_max=-1.000, ||v||₂=1.456
gray_scott vs grokking : spectral=-0.217, sens_mean=-1.000, sens_max=-1.000, ||v||₂=1.431
gray_scott vs may : spectral=+0.974, sens_mean=-1.000, sens_max=-1.000, ||v||₂=1.717
axelrod vs grokking : spectral=+0.140, sens_mean=-0.500, sens_max=-0.500, ||v||₂=0.721
axelrod vs may : spectral=+0.987, sens_mean=-0.448, sens_max=-0.600, ||v||₂=1.239
grokking vs may : spectral=+0.983, sens_mean=+0.067, sens_max=-0.143, ||v||₂=0.996
=== Verdict cross-substrat ===
n_substrats = 4
pairwise_norms = [1.4559, 1.4307, 1.717, 0.7207, 1.2391, 0.9955]
mean_norm_l2 = 1.2598
max_norm_l2 = 1.7170
STABLE_threshold = 0.05, NOISE_threshold = 0.3
VERDICT = NOISE
Lecture 4 – Le verdict : NOISE, un motif de désaccord qui ne tient pas
Les seuils de la décision sont affichés avec le résultat : STABLE_threshold = 0.05, NOISE_threshold = 0.3, VERDICT = NOISE. La moyenne des normes pairwise vaut 1.2598 — 4,2 fois le seuil NOISE, 25 fois le seuil STABLE — et la paire la plus proche (axelrod vs grokking, 0.7207) reste 2,4 fois au-dessus du seuil NOISE. Verdict négatif honnête : les six vecteurs d’obstruction sont grands, aucun ne « recolle » les substrats, et le motif n’est pas assez fin pour distinguer une structure de désaccord d’un artefact d’échantillonnage. La jambe meurt proprement — c’est la discipline de nommage de l’ouverture qui s’applique : pas d’objet nommé derrière ce motif, tant qu’il n’est pas stable.
Le contenu des vecteurs suggère où le désaccord se concentre. Trois paires sur six ont une composante sens_mean à −1.000 — exactement les trois paires qui impliquent gray_scott, le substrat à sensibilité nulle. La composante spectrale est la plus petite en valeur absolue sur toutes les paires (de 0.140 à 0.987) mais elle porte le plus grand écart : gray_scott vs may, +0.974, la paire la plus éloignée (||v||₂ = 1.717, le max du banc). Deux sources de désaccord, une triviale (le substrat plat contre tous les autres) et une structurelle (l’opposition spectrale maximale entre le plat et le canal étroit) — la moyenne les confond, et c’est bien ce que le verdict NOISE enregistre.
Exercice 1 — Quatrième substrat : impact sur le verdict cross-substrat
Le notebook agrège trois substrats (Gray-Scott, Axelrod, Grokking) via cross_substrat_obstruction et obtient un verdict (STABLE / NOISE / INCONCLUSIVE) fondé sur la norme L2 moyenne des vecteurs d’obstruction.
Objectif. Construire une quatrième trajectoire très désordonnée — une marche aléatoire pure sur 4 symboles, sans crossover ni motif — puis l’ajouter au panel et relire le verdict. Un substrat dominé par le bruit doit-il pousser le verdict vers NOISE, ou le motif cross-substrat reste-t-il STABLE malgré lui ?
Indices. - Réutilisez les wrappers spec_gap_wrap, sens_mean_wrap, sens_max_wrap définis dans la cellule « Signatures multi-proxys » (proxys comparables). - proxy_signature(states, n_symbols, spectral_fn=..., sensitivity_mean_fn=..., sensitivity_max_fn=...) renvoie le dict signature. - Reconstruisez substrats_4 = {**substrats, "bruit": sig_bruit} puis appelez cross_substrat_obstruction(substrats_4). - Comparez verdict_4["verdict"] et verdict_4["mean_norm_l2"] au verdict committé sur 3 substrats (verdict).
# Exercice 1 — Quatrième substrat (bruit pur) et impact sur le verdict.# TODO étudiant : générer ~400 pas d'une marche aléatoire uniforme sur 4 symboles# (np.random.default_rng(...).integers(0, 4, size=400)).bruit_states =Nonen_symbols_bruit =4# TODO étudiant : calculer la signature proxy du substrat bruit avec les# MÊMES wrappers que les trois autres (spec_gap_wrap, sens_mean_wrap, sens_max_wrap).sig_bruit =None# TODO étudiant : agréger les 4 substrats et lire le nouveau verdict.# substrats_4 = {**substrats, "bruit": sig_bruit}# verdict_4 = cross_substrat_obstruction(substrats_4)verdict_4 =Noneprint("Exercice 1 à compléter — verdict 4 substrats attendu ici.")
Exercice 1 à compléter — verdict 4 substrats attendu ici.
Lecture 5 – La heatmap : un banc entier au-dessus de la palette
La heatmap donne à lire une conclusion que le tableau ne montre pas : la palette est bornée par max(noise_threshold, 0.5) (voir le code de la cellule), donc à 0.5 ici — et les six normes pairwise (de 0.7207 à 1.7170) dépassent toutes cette borne. Toute la carte est saturée dans la couleur maximale : aucune paire n’apparaît « proche », aucune ne se distingue — la visualisation dit dès le premier regard que le banc entier est en désaccord.
Pour discriminer, il faudrait une palette portée à la gamme réelle des normes (par exemple un vmax autour de 1.717, le max du banc) ; à palette fixée sur le seuil, le « triangle chaud » des trois paires impliquant gray_scott (1.456, 1.431, 1.717) est indiscernable du reste. C’est une direction d’amélioration de la visualisation à noter pour les prochains substrats — pas une correction du verdict : le NOISE de la Lecture 4 ne dépend pas de la palette.
Exercice 2 — Sensibilité du verdict aux seuils stable / noise
Le verdict est une décision à deux seuils : STABLE si mean_norm_l2 <= stable_threshold (défaut 0.05), NOISE si mean_norm_l2 >= noise_threshold (défaut 0.30), INCONCLUSIVE entre les deux. La valeur committée verdict["mean_norm_l2"] est fixée par les trois substrats — mais la classification dépend du choix des seuils.
Objectif. Pour le mean_norm_l2 observé, déterminer la plage de stable_threshold qui ferait basculer le verdict vers STABLE, et la plage de noise_threshold qui le ferait basculer vers NOISE. Cela caractérise la robustesse de la conclusion : un verdict qui change pour un seuil à ±0.01 est fragile ; un verdict invariant sur [0.01, 0.20] est solide.
Indices. - Le verdict se calcule par cross_substrat_obstruction(substrats, stable_threshold=s, noise_threshold=n) — les seuils sont des arguments. - Balayez par ex. stable_threshold dans np.linspace(0.01, 0.50, 25) avec noise_threshold fixé haut (0.99) pour isoler le basculement STABLE. - Notez le plus petit stable_threshold pour lequel verdict == "STABLE".
# Exercice 2 — Robustesse du verdict au choix des seuils.mean_norm_committed = verdict["mean_norm_l2"] # observable fixée par les 3 substratsprint(f"mean_norm_l2 committé = {mean_norm_committed:.4f}")# TODO étudiant : balayer stable_threshold (noise_threshold fixé à 0.99 pour# isoler le basculement vers STABLE) et identifier le seuil critique.bascule_stable =None# plus petit stable_threshold donnant verdict == "STABLE"# TODO étudiant (extension) : balayer de même noise_threshold (stable fixé à 0.0)# pour le basculement vers NOISE.bascule_noise =Noneprint("Exercice 2 à compléter — seuils de basculement attendus ici.")
mean_norm_l2 committé = 1.2598
Exercice 2 à compléter — seuils de basculement attendus ici.
Exercice 3 — Vérifier l’insensibilité à l’échelle de obstruction_vector
La docstring de obstruction_vector revendique une normalisation par |sig_a[k]| + |sig_b[k]| qui rend le vecteur insensible à l’échelle globale : deux substrats dont les proxys diffèrent d’un facteur 10 doivent donner le même vecteur d’obstruction qu’à l’échelle 1. C’est une propriété falsifiable — vérifions-la.
Objectif. (a) Construire sig_gs_x10 = sig_gs avec chaque proxy multiplié par 10. (b) Vérifier que obstruction_vector(sig_gs, sig_gs_x10) est quasi nul (deux signatures proportionnelles → obstruction nulle). (c) Vérifier que obstruction_vector(sig_gs, sig_ax) ≈ obstruction_vector(sig_gs_x10, sig_ax) (le vecteur cross-substrat est inchangé par la remise à l’échelle d’un seul côté).
Indices. - sig_gs est un dict plat {spectral_gap, sensitivity_mean, sensitivity_max, ...}. Construisez sig_gs_x10 = {k: v*10 for k, v in sig_gs.items() if isinstance(v, (int, float))}. - obstruction_vector(a, b) renvoie un dict dont la clé norm_l2 résume l’amplitude ; comparez les norm_l2.
# Exercice 3 — Scale-invariance du vecteur d'obstruction (propriété falsifiable).# TODO étudiant : construire sig_gs_x10 (chaque proxy x10).sig_gs_x10 =None# TODO étudiant : (b) obstruction entre sig_gs et sa version x10 — attendu ~0.vec_proportionnel =None# TODO étudiant : (c) comparer obstruction(sig_gs, sig_ax) vs obstruction(sig_gs_x10, sig_ax).vec_echelle1 =Nonevec_echelle10 =Noneprint("Exercice 3 à compléter — vérification de l'insensibilité à l'échelle.")
Exercice 3 à compléter — vérification de l'insensibilité à l'échelle.
Interprétation
Substrats testés : Gray-Scott (U quantifiée en 4 niveaux sur grille 64x64 intégrée 800 pas), Axelrod (index de la stratégie dominante, alphabet dynamique = nombre de stratégies), Grokking (marche aléatoire biaisée, compression crossover à mi-parcours), May (biomasse du modèle de pâturage bistable discrétisée en 16 quantiles, régime sous le pli).
Proxys calculés : spectral_gap (gap spectral du Laplacien du graphe de transition), sensitivity_mean et sensitivity_max (sur fonction identité f(x) = x - l’observable dérivée du substrat, pas une fonction-test bivalente). Avec f(x) = x%2 (livraison #7578), l’instrument saturait à mean=max=1.0 sur alphabet binaire et mean=max=2.0 sur alphabet quaternaire : la discrimination cross-substrat était artefactuelle. L’identité restaure un panel de valeurs non-plafonné.
Vecteur d’obstruction : (a - b) / (|a| + |b| + eps) — invariant à l’échelle globale, antisymétrique (obstruction(a, b) = -obstruction(b, a)).
Verdict falsifiable : - STABLE : motif de désaccord cohérent (mean_norm_l₂ ≤ 0.05) sur ≥ 2 substrats — preuve empirique de l’objet. - NOISE : motif aléatoire (mean_norm_l₂ ≥ 0.30) — verdict négatif honnête, la jambe meurt proprement. - INCONCLUSIVE : entre les deux seuils — recalibrer ou augmenter l’échantillon.
Suite logique (issue #7395)
Premier livrable concret = au moins un cas où la lecture-obstruction change une décision expérimentale ou une visualisation (issue #7395, acceptance positive). Si le verdict est STABLE ici : à documenter ; si NOISE : à documenter aussi (verdict honnête). Si INCONCLUSIVE : à recalibrer les seuils ou augmenter la taille d’échantillon.
Discipline de nommage (HARD)
L’objet n’est pas encore nommé. Le vocabulaire utilisé reste descriptif (obstruction vector, pairwise norm, STABLE / NOISE). Aucune lettre grecque décorée sur un objet non encore constaté — la stabilisation empirique précède le baptême.