ICT — Annexe : la contextualité du zoo de proxys est un CSP
Annexe spéculative, non numérotée. Registre : issue #7290, dans l’épic ICT #4588. Cette annexe instruit un transfert conceptuel — elle ne le proclame pas. Le résultat qu’elle établit est négatif, et c’est précisément son intérêt.
La question
Le « zoo de proxys » de la série ICT mesure une même trajectoire par plusieurs instruments (écart spectral, sensibilité moyenne, sensibilité maximale), sous plusieurs grains de description (coarse-grainings). Deux instruments peuvent être en désaccord. La question de cette annexe :
Ce désaccord est-il un simple désaccord de valeurs — chaque instrument mesure quelque chose de légèrement différent — ou une obstruction structurelle, au sens où aucune attribution de valeurs cohérente n’existe globalement, bien que chaque instrument soit localement parfaitement cohérent ?
La seconde forme porte un nom en mécanique quantique : c’est la contextualité au sens de Kochen–Specker, formalisée par Abramsky et Brandenburger dans un cadre de faisceaux. Elle a une propriété rare pour un concept importé : elle se calcule. Nous allons voir qu’elle se ramène littéralement à un problème de satisfaction de contraintes (CSP).
Le plan
Traduire le vocabulaire terme à terme, et valider l’instrument sur un cas dont la réponse est prouvée à la main avant de le croire sur des données.
Voir qu’il y a trois crans, pas deux — un SAT nu ne dit pas « non contextuel ».
Installer le garde-fou : trois manières pour un verdict d’être vide.
Mesurer sur le substrat S1, et rendre un verdict honnête.
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.
1. Le dictionnaire
Le cadre d’Abramsky–Brandenburger se transpose terme à terme. C’est ce tableau qui rend le transfert instruit plutôt que décoratif — chaque ligne est une correspondance qu’on peut contester nommément.
Cadre quantique (Kochen–Specker)
Zoo de proxys ICT
Observable
un proxy (spectral_gap, sensitivity_mean, …)
Contexte de mesure (observables co-mesurables)
un protocole : quels proxys sont calculés ensemble
Support de la distribution du contexte
les tuples d’issues effectivement observés
Section locale
une attribution de valeurs cohérente dans un contexte
Section globale
une attribution cohérente simultanément dans tous les contextes
Fortement contextuel
aucune section globale n’existe
La dernière ligne est le cœur calculable : « une section globale existe-t-elle ? » est un CSP à contraintes extensionnelles — une table de tuples autorisés par contexte. En CP-SAT cela s’écrit directement (AddAllowedAssignments).
Valider le détecteur avant de le croire
On ne braque pas un instrument neuf sur des données inconnues. On le braque d’abord sur un cas dont la réponse est prouvée à la main : la boîte de Popescu–Rohrlich, objet canonique fortement contextuel. Son insatisfiabilité se démontre par parité — les quatre contraintes XOR somment à
\[2\,(a_1 + a_2 + b_1 + b_2) \equiv 1 \pmod 2\]
dont le membre de gauche est pair et celui de droite impair. Aucune attribution ne peut donc satisfaire les quatre contextes ensemble.
from ict import proxy_contextuality as pc# La boite PR : 4 contextes, chacun co-mesurant 2 observables sur {0,1}.pr = pc.pr_box_model()print("contextes :")for ctx in pr.contexts:print(f" {ctx.proxies} support = {sorted(ctx.support)}")print(f"\nobservables : {pr.measurements}")print(f"espace de recherche : {pr.search_space}")verdict = pc.contextuality_verdict(pr)print(f"\nsatisfiable : {verdict['satisfiable']}")print(f"VERDICT : {verdict['verdict']}")print(f"CP-SAT : {verdict['cpsat']}")print(f"obstruction minimale : {pc.minimal_obstruction(pr)}")
Le détecteur retrouve strongly_contextual sur un cas dont nous connaissons la réponse par un argument de parité, et les deux voies indépendantes s’accordent : l’énumération exhaustive bornée et CP-SAT. Un désaccord entre elles lèverait une RuntimeError — ce serait un bug d’encodage, jamais un résultat.
Noter que CP-SAT ne rend délibérément aucun témoin. Le statut SAT/UNSAT est déterministe ; le témoin ne l’est pas (le solveur peut renvoyer une solution différente d’une exécution à l’autre). Rapporter un témoin non canonique serait fabriquer de la dérive dans les sorties committées.
L’obstruction minimale répond à une exigence de l’issue : en cas d’obstruction, dire quels protocoles portent le conflit. C’est une sous-famille irréductible — en retirer n’importe quel contexte rend le modèle satisfiable.
2. Trois crans, pas deux
Voici l’erreur qu’un test naïf commet. On construit le CSP, il répond SAT, on conclut « non contextuel ». C’est faux.SAT dit seulement « pas fortement contextuel ».
Abramsky et Brandenburger distinguent un cran intermédiaire, la contextualité logique : des sections globales existent, mais il existe un événement local effectivement observé qu’aucune de ces sections globales ne réalise. L’obstruction n’est pas globale, elle est localisée sur cet événement — et un test SAT nu ne la voit pas.
modeles = {"boite PR": pc.pr_box_model(),"logique mais pas forte": pc.logically_but_not_strongly_contextual_model(),"non contextuel": pc.non_contextual_model(),}print(f"{'modele':<24}{'SAT':<7}{'verdict':<24}{'sections':<10}{'locales sans extension'}")print("-"*92)for nom, m in modeles.items(): v = pc.contextuality_verdict(m) lc = pc.logical_contextuality(m)print(f"{nom:<24}{str(v['satisfiable']):<7}{v['verdict']:<24} "f"{lc['n_global_sections']:<10} "f"{lc['n_without_extension']}/{lc['n_local_sections']}" )
modele SAT verdict sections locales sans extension
--------------------------------------------------------------------------------------------
boite PR False strongly_contextual 0 8/8
logique mais pas forte True logically_contextual 1 5/9
non contextuel True non_contextual 16 0/16
Lecture
La ligne du milieu est celle qui compte. Le modèle est satisfiable — un test naïf aurait conclu « non contextuel » — et pourtant il porte des témoins : des événements observés localement qu’aucune section globale n’étend. Le verdict correct est logically_contextual.
C’est la raison pour laquelle le module rend logical_contextuality séparément : sans ce cran, la moitié des obstructions passent pour des absences d’obstruction.
Exercice 1 — construire un modèle et prédire son cran
Le recouvrement partiel est ce qui rend la question non triviale : trois contextes \(A=\{p,q\}\), \(B=\{q,r\}\), \(C=\{r,p\}\) se chevauchent deux à deux sans qu’aucun ne contienne les trois proxys. C’est la structure en cycle qui porte les obstructions.
Construis un tel modèle avec pc.empirical_model, prédis son cran avant de lancer le test, puis compare. Le format attendu par empirical_model est une liste de mappings {"proxies": [...], "support": [(...), ...]}.
# TODO etudiant : construire un modele a recouvrement partiel en cycle.## Indice 1 : trois contextes A={p,q}, B={q,r}, C={r,p}, issues dans {0,1}.# Indice 2 : pour forcer une obstruction, demande a chaque contexte que ses deux# proxys DIFFERENT (support = [(0,1),(1,0)]). Un cycle impair de# contraintes « different » est insatisfiable -- c'est le meme argument# de parite que la boite PR.# Indice 3 : pour obtenir un modele satisfiable, remplace l'un des trois supports# par [(0,0),(1,1)] (« egaux ») et refais le test.## Etape 1 : ecrire les trois contextescontextes =None# Etape 2 : construire le modele# modele = pc.empirical_model(contextes)# Etape 3 : predire le verdict AVANT de le calculer, puis verifierprediction =Noneprint("Exercice 1 a completer")
Exercice 1 a completer
3. Le garde-fou : trois manières d’être vide
C’est le point méthodologique central de cette annexe, et il vaut bien au-delà d’elle.
L’issue #7290 nomme le risque : « le risque principal est l’analogie décorative » — un appareil impressionnant qui rend un verdict dont la réponse était forcée par la construction. La série ICT en a déjà fait l’expérience avec l’obstruction de Čech (ICT-15d) : verdict TRIVIAL, parce que les sections étaient colinéaires par construction.
La leçon est convertie ici en machinerie plutôt qu’en vigilance : un verdict ne vaut que si sa négation était atteignable. Trois configurations reçoivent donc un verdict distinct de SAT/UNSAT, qui dit « le test n’était pas capable de trancher » :
Verdict de dégénérescence
Ce qui est vide
degenerate_single_cover
Tous les contextes co-mesurent le même ensemble de proxys. La question se réduit à « l’intersection des supports est-elle non vide ? ». Il n’y a pas de contextes distincts, donc pas de contextualité possible.
degenerate_no_overlap
Aucun proxy n’est partagé. SAT est forcé : chaque contexte se satisfait indépendamment.
degenerate_rigid
Tous les supports sont des singletons. Un UNSAT n’est alors qu’un changement de valeur — une dissociation, pas une obstruction. La force de Kochen–Specker est qu’aucune attribution ne marche alors que chaque contexte en admet localement plusieurs.
L’ordre de priorité est single_cover → no_overlap → rigid : on retient la dégénérescence la plus forte, celle qui force le verdict de la façon la plus complète.
construction SAT verdict
--------------------------------------------------------------
recouvrement unique True degenerate_single_cover
aucun recouvrement True degenerate_no_overlap
supports rigides False degenerate_rigid
Lecture — la ligne décisive est la troisième
« supports rigides » est insatisfiable : le contexte 1 impose q = 1, le contexte 2 impose q = 0. Un test naïf aurait rapporté « obstruction structurelle », et cela aurait été un faux positif — pas une erreur de calcul, mais une erreur d’interprétation. Il n’y a là aucune obstruction au sens de Kochen–Specker : simplement deux protocoles qui ne mesurent pas la même chose. Chaque contexte n’admettant qu’une seule section locale, il n’y avait aucune liberté à contraindre.
C’est exactement le faux positif que l’issue redoutait, et le garde l’intercepte.
Exercice 2 — falsifier le garde
Un garde-fou qu’on n’a pas essayé de casser n’est pas un garde-fou. Écris un modèle qui soit à la fois insatisfiable et de supports rigides, mais avec un recouvrement réel (au moins un proxy partagé, et pas le même ensemble de proxys partout).
Vérifie que le verdict rendu est bien degenerate_rigid et jamaisstrongly_contextual. Si tu parviens à obtenir strongly_contextual sur un modèle à supports tous singletons, tu as trouvé un bug : signale-le sur l’issue #7290.
# TODO etudiant : fabriquer un piege pour le garde-fou.## Indice 1 : recouvrement REEL veut dire que les ensembles de proxys ne sont pas# tous identiques (sinon c'est single_cover, une autre degenerescence,# et elle a la priorite).# Indice 2 : supports rigides = chaque support ne contient qu'un seul tuple.# Indice 3 : le test decisif du module s'appelle# test_rigid_supports_unsat_is_not_reported_as_contextual --# tu es en train d'en ecrire une variante.## Etape 1 : construire le modele piegepiege =None# Etape 2 : verifier le verdict et l'assertion attendue# v = pc.contextuality_verdict(pc.empirical_model(piege))# attendu : v["satisfiable"] is False et v["verdict"] == pc.DEGENERATE_RIGIDprint("Exercice 2 a completer")
Exercice 2 a completer
4. La mesure appliquée : le zoo ICT
Tout est en place. On braque maintenant l’instrument validé sur des données réelles.
Protocole. Substrat S1 (SelfSortingArray), trajectoire du désordre mesurée par le nombre d’inversions. Contextes = trois grains de description (\(k \in \{3,4,6\}\) symboles). Graines \(\{0, 1, 7, 42, 99\}\). Les proxys sont câblés comme dans ICT-15c — avec le correctif anti-saturation \(f(x) = x\) — plutôt que recâblés à neuf, pour que la mesure porte sur le zoo tel qu’il est et non sur une variante de circonstance.
Les valeurs continues sont discrétisées en issues binaires par médiane du corpus : c’est un choix, pas une donnée, et le verdict est à lire avec cette réserve.
from ict import sensitivity as SEfrom ict import spectral as SPfrom ict.meta_proxy import proxy_signaturefrom ict.self_sorting import SelfSortingArraydef inversions(v):returnsum(1for i inrange(len(v)) for j inrange(i +1, len(v)) if v[i] > v[j])def trajectory(seed, n_steps=120):# Trajectoire du desordre du substrat S1. arr = SelfSortingArray(values=[5, 3, 8, 1, 9, 2, 7, 4, 6, 0], seed=seed) traj = [inversions(arr.values)]# step() rend False pour un tick sans mouvement, PAS pour une terminaison :# on ne coupe donc pas sur False.for _ inrange(n_steps): arr.step() traj.append(inversions(arr.values))return trajdef discretize(traj, n_symbols):# Coarse-graining : bins de largeur egale sur l'amplitude observee. lo, hi =min(traj), max(traj)if hi == lo:return [0] *len(traj)return [min(n_symbols -1, int((x - lo) / (hi - lo) * n_symbols)) for x in traj]SEEDS = (0, 1, 7, 42, 99)GRAININGS = (3, 4, 6)PROXIES = ("spectral_gap", "sensitivity_mean", "sensitivity_max")signatures = {}for k in GRAININGS: runs = []for seed in SEEDS: states = discretize(trajectory(seed), k) sig = proxy_signature( states, k, spectral_fn=lambda s, n: float(SP.spectral_summary(s, n)["spectral_gap"]), sensitivity_mean_fn=lambda s, n: float( SE.sensitivity_distribution(s, n, lambda x: x)["mean"] ), sensitivity_max_fn=lambda s, n: float( SE.sensitivity_distribution(s, n, lambda x: x)["max"] ), ) runs.append({p: sig[p] for p in PROXIES}) signatures[f"coarse_graining_k{k}"] = runsfor ctx, runs in signatures.items():print(ctx)for seed, r inzip(SEEDS, runs):print(f" seed={seed:>2} gap={r['spectral_gap']:.4f} "f"sens_mean={r['sensitivity_mean']:.4f} sens_max={r['sensitivity_max']:.4f}" )
model, seuils = pc.model_from_signatures(signatures, PROXIES)report = pc.overlap_report(model)out = pc.contextuality_verdict(model)print("Seuils de discretisation (medianes du corpus)")for p, t in seuils.items():print(f" {p:<18}{t:.6f}")print("\nDiagnostic de recouvrement")print(f" contextes : {len(model.contexts)}")print(f" recouvrements distincts : {report['n_distinct_covers']}")print(f" proxys partages : {report['shared_proxies']}")print(f" tailles de support : {report['support_sizes']}")print("\n satisfiable : {}".format(out["satisfiable"]))print(f" CP-SAT : {out['cpsat']}")print(f" VERDICT : {out['verdict']}")
Seuils de discretisation (medianes du corpus)
spectral_gap 0.013437
sensitivity_mean 1.500000
sensitivity_max 2.000000
Diagnostic de recouvrement
contextes : 3
recouvrements distincts : 1
proxys partages : ['sensitivity_max', 'sensitivity_mean', 'spectral_gap']
tailles de support : [1, 2, 1]
satisfiable : False
CP-SAT : {'available': True, 'satisfiable': False, 'status_name': 'INFEASIBLE'}
VERDICT : degenerate_single_cover
Lecture — l’écart entre les deux dernières lignes est le résultat
Le modèle est insatisfiable, et le verdict n’est pas « fortement contextuel ». Cet écart est tout le propos de l’annexe.
Un test naïf aurait rapporté ici « obstruction structurelle dans le zoo de proxys ». Le garde intercepte, et la raison est structurelle — vérifiable sans le solveur : les trois proxys du zoo partagent la même signature d’appel (states, n_symbols). Rien n’empêche donc de les calculer tous les trois sur la même trajectoire. Or c’est précisément la non-co-mesurabilité — l’analogue de la non-commutativité chez Kochen–Specker — qui crée des contextes distincts. Il n’y a qu’un seul recouvrement : degenerate_single_cover.
L’UNSAT observé n’est donc qu’un désaccord entre grains de description : un fait déjà acquis par ailleurs dans la série, et d’une autre nature.
Le verdict honnête
Le zoo ICT n’est pas « non contextuel ». La question de sa contextualité n’y est pas encore posable — le transfert échoue un cran avant le SAT/UNSAT, faute de proxys non conjointement mesurables.
L’ingrédient manquant est identifié, et il est unique : une structure de co-mesurabilité partielle et justifiée — deux proxys dont les prétraitements sont genuinement incompatibles, tels qu’aucun protocole ne les livre ensemble. L’outillage est livré validé : le jour où une telle structure existe, le test se pose sans réécriture.
C’est un résultat négatif, et il est publiable tel quel. Ce qu’il ferme est une piste séduisante ; ce qu’il laisse est un critère net pour la rouvrir.
Un constat secondaire, rapporté à part
Les sorties ci-dessus montrent sensitivity_mean == sensitivity_max == k - 1exactement, sur les 5 graines et les 3 grains. L’instrument sature sur la trajectoire d’inversions de S1 malgré le correctif \(f(x) = x\) d’ICT-15c. Deux des trois proxys ne portent donc aucune variance inter-graines. C’est un fait sur l’instrument, indépendant de Kochen–Specker, et il mérite son propre examen — il n’est pas traité ici.
Exercice 3 — poser la question rendue impossible
C’est l’exercice ouvert, et le seul dont la réponse n’est pas connue.
L’annexe conclut que le zoo manque d’une structure de co-mesurabilité partielle. Propose-en une, puis teste-la.
Un candidat sérieux doit répondre par écrit à : pourquoi ces deux proxys ne peuvent-ils pas être calculés sur la même trajectoire ? Une réponse acceptable invoque une incompatibilité de prétraitement — par exemple deux discrétisations, deux fenêtrages ou deux normalisations qu’aucun pipeline ne peut appliquer simultanément à la même série.
Attention au piège que cette annexe vient de documenter : si ta structure produit un recouvrement unique, le garde rendra degenerate_single_cover et la question sera de nouveau non posable. Il faut un recouvrement partiel en cycle.
# TODO etudiant : proposer une structure de co-mesurabilite partielle JUSTIFIEE.## Indice 1 : la justification est la partie difficile, et elle est textuelle.# Ecris-la en commentaire ci-dessous AVANT de coder quoi que ce soit ;# un decoupage arbitraire des proxys en contextes ne demontrerait rien# (ce serait exactement l'analogie decorative que l'annexe refuse).# Indice 2 : la structure viable est un cycle -- A={p,q}, B={q,r}, C={r,p} --# avec p, q, r deux a deux co-mesurables mais jamais les trois ensemble.# Indice 3 : verifie d'abord pc.overlap_report(...)['n_distinct_covers'] > 1# AVANT de regarder le verdict. Si le recouvrement est unique,# la question n'est pas posee et le verdict ne veut rien dire.## Ma justification (a rediger) :# proxys retenus : ...# incompatibilite : ...# pourquoi elle n'est pas un artefact de mise en oeuvre : ...structure =None# Etape 1 : construire le modele a partir de la structure proposee# Etape 2 : verifier le recouvrement AVANT le verdict# Etape 3 : rendre le verdict et l'interpreter avec les trois cransprint("Exercice 3 a completer")
Exercice 3 a completer
5. Où l’analogie meurt — quatre points de rupture
Un transfert non instruit est l’analogie décorative. Ces quatre points sont les endroits où le cadre quantique et le zoo de proxys cessent de se correspondre, et ils doivent accompagner tout usage de cette machinerie.
Origine des contraintes. Chez Kochen–Specker, les contraintes sont algébriques (relations d’orthogonalité entre projecteurs) : elles sont exactes. Ici, elles sont mesurées — un support est un ensemble de tuples observés. Un UNSAT peut donc être un artefact d’échantillonnage plutôt qu’une obstruction.
Identité des observables. En quantique, « le même observable dans deux contextes » est littéralement le même opérateur. Ici, un proxy calculé sous deux grains de description n’est pas garanti être la même grandeur. C’est l’hypothèse la plus forte du transfert, et elle n’est pas démontrée.
Pas de théorème de dimension. Kochen–Specker exige \(\dim \geq 3\) et des configurations de rayons précises ; rien de tel ne contraint le zoo. Conséquence symétrique et souvent oubliée : l’absence d’obstruction ici n’a aucune portée sur le théorème de Kochen–Specker, ni réciproquement.
Discrétisation. Les seuils qui transforment des valeurs continues en issues discrètes sont un choix. Un verdict doit être rapporté avec sa sensibilité à ce choix, sans quoi il mesure le seuil autant que le substrat.
Ce qu’il faut retenir
La contribution de cette annexe n’est pas un verdict de contextualité — il n’y en a pas. C’est une méthode :
une question conceptuelle importée peut devenir un objet calculable, et c’est ce qui la rend contestable plutôt que décorative ;
un détecteur se valide sur un cas prouvé à la main avant d’être braqué sur des données ;
et surtout : un verdict ne vaut que si sa négation était atteignable. Les trois dégénérescences ne sont pas un raffinement cosmétique — ce sont elles qui ont transformé un faux positif séduisant en un résultat négatif utilisable.
Note de grade C (discipline #8182) : la lecture de la contextualité comme « obstruction au recollement » circule dans la littérature adjacente (boundary problem). Elle est ici un hook documentaire, jamais créditée comme résultat.
Références et suites. Issue #7290 (registre de cette annexe) · module ict/proxy_contextuality.py et sa suite de tests · docs/ict/synthese-invariants-dissociations-obstructions.md (section dédiée) · ICT-15d pour la leçon d’origine sur les verdicts vides.