ManPla commited on
Commit
4d362a0
·
verified ·
1 Parent(s): 748c975

v6.0: la demo viva sustituye a la galeria estatica, en la misma URL

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
.gitattributes CHANGED
@@ -1,35 +1 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
  *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  *.safetensors filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,28 +1,31 @@
1
  ---
2
- title: v1(W_O) is not identifiable
3
- emoji: 🎛️
4
- colorFrom: purple
5
- colorTo: red
6
- sdk: static
 
 
7
  pinned: false
8
- license: apache-2.0
9
  ---
10
 
11
- # The dominant direction of $W_O$ is not identifiable
12
 
13
- Interactive demo (precomputed gallery, static) for *"Same function,
14
- different pruning: the dominant direction of $W_O$ under free, soft,
15
- and hard intervention"*.
 
16
 
17
- Code: <https://github.com/mmunozpl/angular-separation-vit>
18
- Results: <https://huggingface.co/datasets/ManPla/angular-separation-vit-results>
19
- Checkpoints: <https://huggingface.co/ManPla/angular-separation-vit-checkpoints>
20
- DOI: [10.5281/zenodo.21630535](https://doi.org/10.5281/zenodo.21630535)
21
 
22
- Browse 72 precomputed combinations (12 layers × 3 gauge strengths ×
23
- 2 random seeds), all from the real ViT-B/16 seed-42 base checkpoint
24
- (anchor column of the paper), showing that a value-output gauge
25
- transformation moves each head's dominant weight direction $v_1(W_O)$
26
- at will —even flipping which pair of heads looks "most redundant"—
27
- while the OV circuit $W_v W_O$, the only quantity the model's function
28
- actually depends on, stays fixed to machine precision.
 
 
 
 
1
  ---
2
+ title: The value-output gauge orbit
3
+ emoji: 🧭
4
+ colorFrom: indigo
5
+ colorTo: gray
6
+ sdk: gradio
7
+ sdk_version: 6.22.0
8
+ app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
+ # The value-output gauge orbit
13
 
14
+ Interactive demo. The dominant direction of a head's output projection
15
+ moves under a reparameterisation that leaves the model's function
16
+ untouched, and the pruning decision it yields moves with it. The
17
+ identifiable object ---the OV circuit--- does not move.
18
 
19
+ Two models, identical in function, two different prunings. English and
20
+ Spanish; ViT-B/16 and Pythia-410M.
 
 
21
 
22
+ DOI: https://doi.org/10.5281/zenodo.21630534
23
+
24
+ ---
25
+
26
+ # La órbita de gauge valor-salida
27
+
28
+ Demo interactiva. La dirección dominante de la proyección de salida de
29
+ una cabeza se desplaza bajo una reparametrización que deja la función
30
+ del modelo intacta, y la decisión de poda que produce cambia con ella.
31
+ El objeto identificable ---el circuito OV--- no se mueve.
app.py ADDED
@@ -0,0 +1,753 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """demo interactiva de la órbita de gauge valor-salida.
2
+
3
+ tres pestañas: la órbita con el conmutador ortogonal/genérico, la
4
+ decisión de poda bajo gauge, y qué se mide. toda la aritmética la
5
+ ejecuta el código del paper ---`src/gauge_flip.py`,
6
+ `scripts/run_fase_G.py`, `scripts/decision_rota.py`--- sobre el
7
+ portador ligero de `demo/portador.py`; aquí no se reimplementa nada.
8
+ """
9
+
10
+ import math
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ import gradio as gr
15
+ import matplotlib
16
+ import torch
17
+ from tqdm import tqdm
18
+
19
+ matplotlib.use("Agg") # sin servidor gráfico en el Space
20
+
21
+ from matplotlib.figure import Figure # noqa: E402
22
+ from matplotlib.patches import Rectangle # noqa: E402
23
+
24
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
25
+
26
+ from demo.portador import (carga, verifica_manifiesto, # noqa: E402
27
+ verifica_relleno)
28
+ from src.firma_funcional import w_v_columnas # noqa: E402
29
+ from src.gauge_flip import (aplica_gauge_ortogonal, # noqa: E402
30
+ aplica_gauge_ov)
31
+ from src.nucleo_lectura import decisiones, firma_exacta # noqa: E402
32
+
33
+ DOI = "https://doi.org/10.5281/zenodo.21630534"
34
+ # gr.Markdown no renderiza matemáticas salvo que se le declaren los
35
+ # delimitadores: sin esto la prosa enseña los dólares en crudo, justo
36
+ # al lado de una figura que matplotlib sí compone bien
37
+ LATEX = [{"left": "$$", "right": "$$", "display": True},
38
+ {"left": "$", "right": "$", "display": False}]
39
+ # clave = escala_id (el mando real); el rótulo cambia con el idioma y
40
+ # con el locale del número, la clave no
41
+ FUERZAS = {128.0: ("muy débil (0,06)", "very weak (0.06)"),
42
+ 64.0: ("débil (0,13)", "weak (0.13)"),
43
+ 32.0: ("media (0,25)", "medium (0.25)"),
44
+ 16.0: ("fuerte (0,50)", "strong (0.50)"),
45
+ 8.0: ("saturada (1,00)", "saturated (1.00)"),
46
+ 2.0: ("muy fuerte (4,05)", "very strong (4.05)")}
47
+
48
+
49
+ def op_fuerza(idi: str):
50
+ """opciones del desplegable de fuerza en un idioma.
51
+
52
+ Args:
53
+ idi: 'es' o 'en'.
54
+
55
+ Returns:
56
+ lista de pares (rótulo, valor) para gradio.
57
+ """
58
+ j = 0 if idi == "es" else 1
59
+ return [(v[j], k) for k, v in FUERZAS.items()]
60
+ # por columna: (etiqueta del sector, k de poda, referencia de la tabla 3).
61
+ # el k es el 25 % relativo de cada una, y la referencia es la medida
62
+ # publicada de esa columna ---nunca la de la otra---
63
+ COLUMNAS = {
64
+ "vitb": {"es": "ViT-B/16 (visión)", "en": "ViT-B/16 (vision)",
65
+ "k": 3, "ref": ("92,7 % / 0,378", "92.7 % / 0.378")},
66
+ "pythia": {"es": "Pythia-410M (lenguaje)",
67
+ "en": "Pythia-410M (language)",
68
+ "k": 4, "ref": ("90,0 % / 0,379", "90.0 % / 0.379")},
69
+ }
70
+
71
+
72
+ def op_tipo(idi: str):
73
+ """opciones del mando de tipo de gauge en un idioma.
74
+
75
+ el valor viaja en clave estable ('gen'/'ort') porque gradio valida
76
+ la entrada en el servidor contra las opciones declaradas al
77
+ construir el bloque, y `gr.update` no las cambia allí: si el valor
78
+ fuese la palabra traducida, el mando quedaría roto en inglés.
79
+
80
+ Args:
81
+ idi: 'es' o 'en'.
82
+
83
+ Returns:
84
+ lista de pares (rótulo, clave) para gradio.
85
+ """
86
+ return [(T[idi]["gen"], "gen"), (T[idi]["ort"], "ort")]
87
+
88
+
89
+ def op_columna(idi: str):
90
+ """opciones del desplegable de columna en un idioma.
91
+
92
+ Args:
93
+ idi: 'es' o 'en'.
94
+
95
+ Returns:
96
+ lista de pares (rótulo, clave) para gradio.
97
+ """
98
+ return [(v[idi], k) for k, v in COLUMNAS.items()]
99
+
100
+ # textos en las dos lenguas. el español es el canónico y el inglés su
101
+ # derivado, con la terminología del glosario del paper: soft probe,
102
+ # hard variant, response signature, static invariant, band, gauge.
103
+ # ojo al locale de los números: coma en es, punto en en
104
+ T = {
105
+ "es": {
106
+ "titulo": "# La órbita de gauge valor-salida\nDos modelos "
107
+ "idénticos en función, dos geometrías distintas.",
108
+ "tab1": "Órbita", "tab2": "Decisión rota", "tab3": "Qué se mide",
109
+ "figb": "El abismo, cabeza a cabeza",
110
+ "figb_t": "Capa {capa}: lo que el gauge mueve y lo que no",
111
+ "figb_1": r"$\Delta v_1(W_O)$ [grados]",
112
+ "figb_2": r"$1-|\cos|$ del circuito OV",
113
+ "figb_m": "media", "figb_0": "0 exacto",
114
+ "figb_s": "suelo de arccos en doble precisión",
115
+ "figb_n": "eje común en escala logarítmica: la distancia "
116
+ "horizontal entre los dos bloques es el abismo",
117
+ "btn3": "Barrer fuerzas",
118
+ "figs": "Barrido de fuerzas",
119
+ "figs_t": "Capa {capa}, semilla {sem}: deriva contra la "
120
+ "fuerza del gauge",
121
+ "figs_x": "desviación de R respecto a un múltiplo escalar",
122
+ "fig": "Similitud entre cabezas",
123
+ "fig_t": "Capa {capa}: |cos| entre las direcciones "
124
+ "$v_1(W_O)$ de cada par de cabezas",
125
+ "fig_a": "Antes del gauge", "fig_d": "Después del gauge",
126
+ "fig_e": "cabeza", "fig_p": "par elegido",
127
+ "col": "Columna", "capa": "Capa", "sem": "Semilla del gauge",
128
+ "fuerza": "Fuerza (desv. de R al escalar)", "tipo": "Tipo de gauge",
129
+ "gen": "genérico", "ort": "ortogonal",
130
+ "btn1": "Muestrear gauge", "btn2": "Decidir bajo gauge",
131
+ "porcabeza": "Por cabeza",
132
+ "cab": ["cabeza", "Δ v1(W_O) [°]", "1-|cos| circuito OV"],
133
+ "cab_d": "**{col}, capa {capa}, semilla {sem}, k={k}.**\n",
134
+ "res": "**{col}, capa {capa}, gauge {tipo}, semilla {sem}.**\n\n"
135
+ "- Desplazamiento medio de $v_1(W_O)$: **{gp}°**\n"
136
+ "- Desviación media del circuito OV (invariante), "
137
+ "en la métrica que certifica el paper: "
138
+ "$1-|\\cos|$ = **{gi}**\n\nMisma libertad de gauge, dos "
139
+ "resultados: {ver}. El circuito OV no se mueve en ningún "
140
+ "caso ---es el objeto identificable---.",
141
+ "ver_ort": "el subgrupo ortogonal **no la mueve**: la lectura "
142
+ "por pesos sobrevive intacta",
143
+ "ver_gen": "la parte no ortogonal **sí la mueve**, decenas de "
144
+ "grados",
145
+ "cols_d": ["criterio", "par antes", "par después", "¿cambia?",
146
+ "solape"],
147
+ "crit_p": "por pesos $v_1(W_O)$", "crit_i": "por el invariante",
148
+ "si": "**sí**", "no": "no",
149
+ "contador": "**En esta sesión**, la decisión por pesos ha "
150
+ "cambiado en {c} de {n} gauges ({pct:.0f} %). La "
151
+ "medida publicada para esta columna es {ref} (par / "
152
+ "solape); la decisión por el invariante no cambia "
153
+ "nunca.",
154
+ },
155
+ "en": {
156
+ "titulo": "# The value-output gauge orbit\nTwo models identical "
157
+ "in function, two different geometries.",
158
+ "tab1": "Orbit", "tab2": "Broken decision",
159
+ "tab3": "What is measured",
160
+ "figb": "The gap, head by head",
161
+ "figb_t": "Layer {capa}: what the gauge moves and what it "
162
+ "does not",
163
+ "figb_1": r"$\Delta v_1(W_O)$ [degrees]",
164
+ "figb_2": r"$1-|\cos|$ of the OV circuit",
165
+ "figb_m": "mean", "figb_0": "exactly 0",
166
+ "figb_s": "arccos floor in double precision",
167
+ "figb_n": "shared logarithmic axis: the horizontal distance "
168
+ "between the two blocks is the gap",
169
+ "btn3": "Sweep strengths",
170
+ "figs": "Strength sweep",
171
+ "figs_t": "Layer {capa}, seed {sem}: drift against gauge "
172
+ "strength",
173
+ "figs_x": "deviation of R from a scalar multiple",
174
+ "fig": "Similarity across heads",
175
+ "fig_t": "Layer {capa}: |cos| between the $v_1(W_O)$ "
176
+ "directions of every pair of heads",
177
+ "fig_a": "Before the gauge", "fig_d": "After the gauge",
178
+ "fig_e": "head", "fig_p": "selected pair",
179
+ "col": "Column", "capa": "Layer", "sem": "Gauge seed",
180
+ "fuerza": "Strength (deviation of R from a scalar)",
181
+ "tipo": "Gauge type", "gen": "generic", "ort": "orthogonal",
182
+ "btn1": "Sample a gauge", "btn2": "Decide under gauge",
183
+ "porcabeza": "Per head",
184
+ "cab": ["head", "Δ v1(W_O) [°]", "1-|cos| OV circuit"],
185
+ "cab_d": "**{col}, layer {capa}, seed {sem}, k={k}.**\n",
186
+ "res": "**{col}, layer {capa}, {tipo} gauge, seed {sem}.**\n\n"
187
+ "- Mean displacement of $v_1(W_O)$: **{gp}°**\n"
188
+ "- Mean deviation of the OV circuit (invariant), "
189
+ "in the metric the paper certifies: "
190
+ "$1-|\\cos|$ = **{gi}**\n\nThe same gauge freedom, "
191
+ "two outcomes: "
192
+ "{ver}. The OV circuit does not move in either case "
193
+ "---it is the identifiable object---.",
194
+ "ver_ort": "the orthogonal subgroup **does not move it**: the "
195
+ "weight reading survives intact",
196
+ "ver_gen": "the non-orthogonal part **does move it**, by tens "
197
+ "of degrees",
198
+ "cols_d": ["criterion", "pair before", "pair after", "changes?",
199
+ "overlap"],
200
+ "crit_p": "by weights $v_1(W_O)$", "crit_i": "by the invariant",
201
+ "si": "**yes**", "no": "no",
202
+ "contador": "**In this session**, the decision by weights has "
203
+ "changed in {c} of {n} gauges ({pct:.0f} %). The "
204
+ "published measurement for this column is {ref} "
205
+ "(pair / overlap); the decision by the invariant "
206
+ "never changes.",
207
+ },
208
+ }
209
+ # el inglés va primero: es el idioma de partida de la interfaz
210
+ IDIOMAS = {"English": "en", "Español": "es"}
211
+
212
+
213
+ def num(x: float, idi: str, dec: int = 3) -> str:
214
+ """formatea un número con el separador decimal del idioma.
215
+
216
+ Args:
217
+ x: valor.
218
+ idi: 'es' o 'en'.
219
+ dec: decimales.
220
+
221
+ Returns:
222
+ el número como texto, con coma o punto según el idioma.
223
+ """
224
+ s = f"{x:.{dec}f}"
225
+ return s.replace(".", ",") if idi == "es" else s
226
+
227
+
228
+ _CACHE: dict = {}
229
+
230
+ verifica_manifiesto() # el código vendido, firmado contra su commit
231
+
232
+
233
+ def cientifico(x: float, idi: str) -> str:
234
+ """notación científica con el separador decimal del idioma.
235
+
236
+ Args:
237
+ x: el valor.
238
+ idi: 'es' o 'en'.
239
+
240
+ Returns:
241
+ la cadena formateada.
242
+ """
243
+ s = f"{x:.1e}"
244
+ return s.replace(".", ",") if idi == "es" else s
245
+
246
+
247
+ def portador(col: str):
248
+ """carga perezosa del portador de una columna, con su contrato.
249
+
250
+ Args:
251
+ col: etiqueta de columna de `COLUMNAS`.
252
+
253
+ Returns:
254
+ el portador ya verificado.
255
+ """
256
+ if col not in _CACHE:
257
+ p = carga(col)
258
+ verifica_relleno(p) # el contrato, vigilado también aquí
259
+ _CACHE[col] = p
260
+ return _CACHE[col]
261
+
262
+
263
+ def capas_de(col: str):
264
+ """actualiza el desplegable de capas al cambiar de columna.
265
+
266
+ Args:
267
+ col: etiqueta de columna.
268
+
269
+ Returns:
270
+ actualización de gradio con las capas de esa columna.
271
+ """
272
+ n = len(portador(col).blocks)
273
+ return gr.update(choices=list(range(n)), value=min(5, n - 1))
274
+
275
+
276
+ def _v1_pesos(port, capa: int) -> torch.Tensor:
277
+ """dirección dominante de w_o por cabeza.
278
+
279
+ Args:
280
+ port: portador del sector.
281
+ capa: índice de capa.
282
+
283
+ Returns:
284
+ tensor [h, d] de direcciones unitarias.
285
+ """
286
+ # sin bajar a simple: el coseno entre dos direcciones
287
+ # casi iguales satura en fp32 y el invariante aparece
288
+ # moviéndose centésimas de grado que no existen
289
+ return firma_exacta(port.w_o_por_cabeza(capa).double(),
290
+ "der")
291
+
292
+
293
+ def _v1_invariante(port, capa: int) -> torch.Tensor:
294
+ """dirección dominante del circuito ov, invariante de gauge.
295
+
296
+ Args:
297
+ port: portador del sector.
298
+ capa: índice de capa.
299
+
300
+ Returns:
301
+ tensor [h, d] de direcciones unitarias.
302
+ """
303
+ w_o = port.w_o_por_cabeza(capa).double()
304
+ w_v = w_v_columnas(port, capa, port.n_cabezas,
305
+ port.dim_cabeza).double()
306
+ r = torch.linalg.qr(w_v, mode="reduced")[1]
307
+ return firma_exacta(r @ w_o, "der")
308
+
309
+
310
+ def _con_gauge(col: str, capa: int, semilla: int,
311
+ fuerza: float, tipo: str):
312
+ """devuelve una copia del portador con un gauge aplicado.
313
+
314
+ Args:
315
+ capa: capa sobre la que actuar.
316
+ semilla: semilla del generador de R.
317
+ fuerza: escala del término identidad de R (ignorada si
318
+ el gauge es ortogonal, que no tiene intensidad).
319
+ tipo: 'gen' u 'ort'.
320
+
321
+ Returns:
322
+ tupla (portador con el gauge aplicado, desviación media de R
323
+ respecto a su mejor múltiplo escalar; 0 si es ortogonal, que
324
+ no tiene intensidad).
325
+ """
326
+ q = portador(col).copia()
327
+ if tipo == "ort":
328
+ aplica_gauge_ortogonal(q, capa, q.n_cabezas, q.dim_cabeza,
329
+ semilla=semilla)
330
+ return q, 0.0
331
+ desv = aplica_gauge_ov(q, capa, q.n_cabezas, q.dim_cabeza,
332
+ semilla=semilla, escala_id=fuerza)
333
+ return q, desv
334
+
335
+
336
+ def _mapa_cos(v: torch.Tensor):
337
+ """|cos| entre todas las parejas de direcciones de una capa.
338
+
339
+ Args:
340
+ v: tensor [h, d] de direcciones unitarias.
341
+
342
+ Returns:
343
+ tupla (matriz [h, h] en numpy, par de máximo fuera de la
344
+ diagonal).
345
+ """
346
+ g = (v @ v.t()).abs().clamp(max=1.0)
347
+ m = g.clone()
348
+ m.fill_diagonal_(-1.0)
349
+ i = int(m.argmax())
350
+ return g.numpy(), (i // g.shape[0], i % g.shape[0])
351
+
352
+
353
+ def figura_cos(antes: torch.Tensor, desp: torch.Tensor, capa: int,
354
+ idi: str) -> Figure:
355
+ """dibuja la matriz de similitud antes y después del gauge.
356
+
357
+ es la misma lectura que produce la decisión de poda: la celda más
358
+ brillante fuera de la diagonal es el par que se podaría. verla
359
+ moverse entre los dos paneles es la afirmación del paper hecha
360
+ imagen.
361
+
362
+ Args:
363
+ antes: direcciones [h, d] antes del gauge.
364
+ desp: direcciones [h, d] después.
365
+ capa: índice de capa, para el título.
366
+ idi: 'es' o 'en'.
367
+
368
+ Returns:
369
+ la figura de matplotlib, lista para `gr.Plot`.
370
+ """
371
+ d = T[idi]
372
+ fig = Figure(figsize=(9.2, 4.2), dpi=110)
373
+ ejes = fig.subplots(1, 2)
374
+ for ax, x, sub in ((ejes[0], antes, d["fig_a"]),
375
+ (ejes[1], desp, d["fig_d"])):
376
+ g, par = _mapa_cos(x)
377
+ im = ax.imshow(g, cmap="magma", vmin=0.0, vmax=1.0)
378
+ for i, j in (par, par[::-1]):
379
+ ax.add_patch(Rectangle((j - 0.5, i - 0.5), 1, 1,
380
+ fill=False, edgecolor="#39d353",
381
+ lw=1.8))
382
+ ax.set_title(f"{sub} \u00b7 {d['fig_p']} "
383
+ f"{tuple(sorted(par))}",
384
+ fontsize=10)
385
+ ax.set_xlabel(d["fig_e"], fontsize=9)
386
+ ax.set_ylabel(d["fig_e"], fontsize=9)
387
+ ax.tick_params(labelsize=8)
388
+ fig.colorbar(im, ax=ejes, fraction=0.032, pad=0.02)
389
+ fig.suptitle(d["fig_t"].format(capa=capa), fontsize=11)
390
+ return fig
391
+
392
+
393
+ def figura_abismo(gp: torch.Tensor, gi: torch.Tensor, capa: int,
394
+ idi: str) -> Figure:
395
+ """dibuja la separación entre lo que se mueve y lo que no.
396
+
397
+ la tabla por cabeza dice que $v_1(W_O)$ deriva decenas de grados y
398
+ que el circuito OV se queda en 1e-16, pero eso hay que leerlo. con
399
+ los dos bloques de barras sobre un mismo eje logarítmico, el
400
+ abismo ---dieciocho órdenes de magnitud--- es distancia en la
401
+ pantalla. bajo el conmutador ortogonal las barras de arriba
402
+ desaparecen y las de abajo no se inmutan: c1 en una imagen.
403
+
404
+ Args:
405
+ gp: desplazamiento de v1(w_o) por cabeza, en grados.
406
+ gi: desviación 1-|cos| del circuito ov por cabeza.
407
+ capa: índice de capa, para el título.
408
+ idi: 'es' o 'en'.
409
+
410
+ Returns:
411
+ la figura de matplotlib, lista para `gr.Plot`.
412
+ """
413
+ d = T[idi]
414
+ n = gp.shape[0]
415
+ y = list(range(n))
416
+ fig = Figure(figsize=(9.2, 0.9 + 0.42 * n), dpi=110)
417
+ ejes = fig.subplots(2, 1, sharex=True)
418
+ for ax, v, etq, color in ((ejes[0], gp, d["figb_1"], "#c4432b"),
419
+ (ejes[1], gi, d["figb_2"], "#2b6cc4")):
420
+ ax.barh(y, v.numpy(), height=0.72, color=color)
421
+ # un valor exactamente nulo no dibuja barra en escala
422
+ # logarítmica y se leería como dato ausente; se rotula, que es
423
+ # justo lo que ocurre con el gauge ortogonal en el panel de
424
+ # arriba: cero exacto, no falta de medida
425
+ for h in y:
426
+ if float(v[h]) == 0.0:
427
+ ax.text(2e-18, h, d["figb_0"], va="center",
428
+ ha="left", fontsize=7, color=color)
429
+ ax.set_ylabel(d["fig_e"], fontsize=9)
430
+ ax.set_yticks(y)
431
+ ax.set_yticklabels([str(h) for h in y], fontsize=7)
432
+ ax.invert_yaxis()
433
+ ax.grid(axis="x", ls=":", lw=0.6, alpha=0.5)
434
+ ax.set_title(f"{etq} · {d['figb_m']} "
435
+ f"{float(v.mean()):.2e}", fontsize=10)
436
+ # el panel de grados tiene suelo: arccos cerca de 1 pierde la
437
+ # mitad de los dígitos, así que sqrt(2*eps) rad es lo mínimo
438
+ # medible. sin la línea, las barras del gauge ortogonal ---que
439
+ # deben ser cero--- se leerían como una deriva de 1e-6 grados
440
+ suelo = math.degrees(math.sqrt(2 * torch.finfo(torch.float64).eps))
441
+ ejes[0].axvline(suelo, ls="--", lw=1.0, color="#555555")
442
+ ejes[0].text(suelo * 1.4, n - 0.4, d["figb_s"],
443
+ fontsize=7, color="#555555", va="center")
444
+ # el eje común es lo que convierte dos medidas en un abismo; el
445
+ # suelo baja hasta la precisión de máquina para que el bloque de
446
+ # abajo tenga dónde caber
447
+ ejes[1].set_xscale("log")
448
+ ejes[1].set_xlim(1e-18, 1e3)
449
+ ejes[1].set_xlabel(d["figb_n"], fontsize=8)
450
+ fig.suptitle(d["figb_t"].format(capa=capa), fontsize=11)
451
+ fig.tight_layout()
452
+ return fig
453
+
454
+
455
+ def barrido(idioma: str, col: str, capa: int, semilla: int) -> Figure:
456
+ """recorre las seis fuerzas y dibuja la deriva contra cada una.
457
+
458
+ es la tabla 2 del paper dibujándose en vivo: la deriva de
459
+ $v_1(W_O)$ crece con la desviación de R respecto a un escalar,
460
+ mientras el circuito OV se queda plano en el suelo de la máquina.
461
+ cuesta seis gauges por pulsación, y por eso tiene botón propio.
462
+
463
+ Args:
464
+ idioma: rótulo del selector.
465
+ col: clave de columna.
466
+ capa: capa a interrogar.
467
+ semilla: semilla del gauge.
468
+
469
+ Returns:
470
+ la figura de matplotlib, lista para `gr.Plot`.
471
+ """
472
+ idi = IDIOMAS[idioma]
473
+ d = T[idi]
474
+ p = portador(col)
475
+ antes_p, antes_i = _v1_pesos(p, capa), _v1_invariante(p, capa)
476
+ xs, ys_p, ys_i = [], [], []
477
+ for escala in tqdm(sorted(FUERZAS, reverse=True),
478
+ desc=f"barrido {col} L{capa}", leave=False):
479
+ q, desv = _con_gauge(col, capa, semilla, escala, "gen")
480
+ cp = _cos_abs(antes_p, _v1_pesos(q, capa))
481
+ ci = _cos_abs(antes_i, _v1_invariante(q, capa))
482
+ xs.append(desv)
483
+ ys_p.append(float(torch.rad2deg(torch.arccos(cp)).mean()))
484
+ ys_i.append(float((1.0 - ci).mean()))
485
+ fig = Figure(figsize=(8.4, 5.4), dpi=110)
486
+ ejes = fig.subplots(2, 1, sharex=True)
487
+ # dos paneles y no un eje común: sobre veintiuna décadas el
488
+ # crecimiento de v1 ---de veinte a ochenta grados--- sería
489
+ # invisible. arriba la subida, abajo la planitud, cada una en su
490
+ # escala; el abismo entre ambas lo cuenta la otra figura
491
+ ejes[0].plot(xs, ys_p, "o-", color="#c4432b")
492
+ ejes[0].set_ylim(0.0, 92.0)
493
+ ejes[0].set_ylabel(d["figb_1"], fontsize=9)
494
+ ejes[1].plot(xs, ys_i, "s-", color="#2b6cc4")
495
+ ejes[1].set_yscale("log")
496
+ ejes[1].set_ylim(1e-18, 1e-12)
497
+ ejes[1].set_ylabel(d["figb_2"], fontsize=9)
498
+ ejes[1].set_xscale("log")
499
+ ejes[1].set_xlabel(d["figs_x"], fontsize=9)
500
+ for ax in ejes:
501
+ ax.grid(ls=":", lw=0.6, alpha=0.5)
502
+ ax.tick_params(labelsize=8)
503
+ fig.suptitle(d["figs_t"].format(capa=capa, sem=semilla),
504
+ fontsize=11)
505
+ fig.tight_layout()
506
+ return fig
507
+
508
+
509
+ def _cos_abs(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
510
+ """|cos| entre direcciones emparejadas por cabeza.
511
+
512
+ Args:
513
+ a: direcciones [h, d].
514
+ b: direcciones [h, d].
515
+
516
+ Returns:
517
+ tensor [h] con el coseno en valor absoluto, acotado a 1.
518
+ """
519
+ return (a * b).sum(1).abs().clamp(max=1.0)
520
+
521
+
522
+ def orbita(idioma: str, col: str, capa: int, semilla: int,
523
+ fuerza: float, tipo: str):
524
+ """mide cuánto mueve el gauge la lectura por pesos y el invariante.
525
+
526
+ Args:
527
+ capa: capa a interrogar.
528
+ semilla: semilla del gauge.
529
+ fuerza: escala del término identidad de R.
530
+ tipo: 'gen' u 'ort'.
531
+
532
+ Returns:
533
+ tupla (resumen en markdown, tabla por cabeza, figura del
534
+ abismo).
535
+ """
536
+ p = portador(col)
537
+ antes_p, antes_i = _v1_pesos(p, capa), _v1_invariante(p, capa)
538
+ q, _ = _con_gauge(col, capa, semilla, fuerza, tipo)
539
+ desp_p, desp_i = _v1_pesos(q, capa), _v1_invariante(q, capa)
540
+ # v1 en grados, que es la magnitud interpretable; el invariante en
541
+ # 1-|cos|, porque arccos cerca de 1 pierde la mitad de los dígitos
542
+ # y pondría un suelo de ~1e-6 grados donde no hay movimiento
543
+ gp = torch.rad2deg(torch.arccos(_cos_abs(antes_p, desp_p)))
544
+ gi = 1.0 - _cos_abs(antes_i, desp_i)
545
+ d = T[IDIOMAS[idioma]]
546
+ idi = IDIOMAS[idioma]
547
+ filas = [[h, num(float(gp[h]), idi, 2),
548
+ cientifico(float(gi[h]), idi)]
549
+ for h in range(p.n_cabezas)]
550
+ ver = d["ver_ort"] if tipo == "ort" else d["ver_gen"]
551
+ cient = cientifico(float(gi.mean()), idi)
552
+ resumen = d["res"].format(
553
+ col=COLUMNAS[col][idi], capa=capa, tipo=d[tipo],
554
+ sem=semilla,
555
+ gp=num(float(gp.mean()), idi, 2),
556
+ gi=cient, ver=ver)
557
+ return resumen, filas, figura_abismo(gp, gi, capa, idi)
558
+
559
+
560
+ def decision(idioma: str, col: str, capa: int, semilla: int,
561
+ fuerza: float, estado: dict):
562
+ """materializa la decisión de poda antes y después del gauge.
563
+
564
+ Args:
565
+ capa: capa a interrogar.
566
+ semilla: semilla del gauge.
567
+ fuerza: escala del término identidad de R.
568
+ estado: contador acumulado de la sesión.
569
+
570
+ Returns:
571
+ tupla (markdown, estado actualizado, mapa de similitud).
572
+ """
573
+ p = portador(col)
574
+
575
+ idi = IDIOMAS[idioma]
576
+ d = T[idi]
577
+ k = COLUMNAS[col]["k"]
578
+ ref = COLUMNAS[col]["ref"][0 if idi == "es" else 1]
579
+ nombre_col = COLUMNAS[col][idi]
580
+ q, _ = _con_gauge(col, capa, semilla, fuerza, "gen")
581
+ lineas = [d["cab_d"].format(col=nombre_col, capa=capa,
582
+ sem=semilla, k=k),
583
+ "| " + " | ".join(d["cols_d"]) + " |",
584
+ "|---|---|---|---|---|"]
585
+ for nombre, f in ((d["crit_p"], _v1_pesos),
586
+ (d["crit_i"], _v1_invariante)):
587
+ par0, top0 = decisiones(f(p, capa), k)
588
+ par1, top1 = decisiones(f(q, capa), k)
589
+ sol = len(set(top0) & set(top1)) / k
590
+ cambia = par1 != par0
591
+ if nombre == d["crit_p"]:
592
+ estado["n"] = estado.get("n", 0) + 1
593
+ estado["c"] = estado.get("c", 0) + int(cambia)
594
+ lineas.append(f"| {nombre} | {par0} | {par1} | "
595
+ f"{d['si'] if cambia else d['no']} | "
596
+ f"{num(sol, idi, 2)} |")
597
+ pct = 100 * estado["c"] / estado["n"]
598
+ lineas.append("")
599
+ lineas.append(d["contador"].format(c=estado["c"],
600
+ n=estado["n"], pct=pct,
601
+ ref=ref))
602
+ # el mapa vive aquí y no en la órbita: es la lectura de la que
603
+ # sale esta tabla, y verlo debajo convierte la fila «cambia: sí»
604
+ # en algo comprobable en vez de en un acto de fe
605
+ mapa = figura_cos(_v1_pesos(p, capa), _v1_pesos(q, capa),
606
+ capa, idi)
607
+ return "\n".join(lineas), estado, mapa
608
+
609
+
610
+ QSM = {"es": f"""
611
+ ### Qué se mide aquí
612
+
613
+ Una cabeza de atención escribe en el flujo residual a través de dos
614
+ matrices, $W_v$ y $W_O$. Esa factorización **no es única**: para
615
+ cualquier $R$ invertible, sustituir $W_v \\to W_v R$ y
616
+ $W_O \\to R^{{-1}} W_O$ deja la función del modelo exactamente igual,
617
+ porque el producto $W_v W_O$ ---el circuito OV--- no cambia.
618
+
619
+ La dirección dominante $v_1(W_O)$ **no** es función de ese producto,
620
+ así que se mueve con $R$ mientras el modelo calcula lo mismo: su
621
+ órbita bajo el gauge es la esfera unitaria completa de su espacio
622
+ fila, y por tanto **ningún umbral de similitud sobre esa dirección
623
+ admite radio certificado positivo**.
624
+
625
+ Un matiz que la primera pestaña enseña: el subgrupo **ortogonal** deja
626
+ $v_1(W_O)$ quieta. Es la parte **no ortogonal** del gauge la que la
627
+ mueve, y está presente en cualquier reparametrización genérica.
628
+
629
+ Los pesos son los de un ViT-B/16 afinado en ImageNet-100 (semilla 42)
630
+ y los de Pythia-410M sin entrenar nada. Cada gauge muestreado es
631
+ reproducible: basta repetir la semilla.
632
+
633
+ Código, datos y certificación de la órbita: <{DOI}>
634
+ """, "en": f"""
635
+ ### What is measured here
636
+
637
+ An attention head writes into the residual stream through two
638
+ matrices, $W_v$ and $W_O$. That factorization is **not unique**: for
639
+ any invertible $R$, substituting $W_v \\to W_v R$ and
640
+ $W_O \\to R^{{-1}} W_O$ leaves the model's function exactly as it was,
641
+ because the product $W_v W_O$ ---the OV circuit--- does not change.
642
+
643
+ The dominant direction $v_1(W_O)$ is **not** a function of that
644
+ product, so it moves with $R$ while the model computes the same
645
+ thing: its orbit under the gauge is the full unit sphere of its row
646
+ space, and therefore **no similarity threshold on that direction
647
+ admits a positive certified radius**.
648
+
649
+ One nuance the first tab shows: the **orthogonal** subgroup leaves
650
+ $v_1(W_O)$ still. It is the **non-orthogonal** part of the gauge that
651
+ moves it, and it is present in any generic reparametrization.
652
+
653
+ The weights are those of a ViT-B/16 fine-tuned on ImageNet-100
654
+ (seed 42) and of Pythia-410M with no training at all. Every sampled
655
+ gauge is reproducible: just repeat the seed.
656
+
657
+ Code, data and the orbit certification: <{DOI}>
658
+ """}
659
+
660
+
661
+ def cambia_idioma(idioma: str):
662
+ """rehace etiquetas y textos estáticos en el idioma elegido.
663
+
664
+ Args:
665
+ idioma: clave de `IDIOMAS`.
666
+
667
+ Returns:
668
+ tupla de actualizaciones de gradio, en el orden de los
669
+ componentes que dependen del idioma (rótulos de pestaña
670
+ incluidos, al final).
671
+ """
672
+ idi = IDIOMAS[idioma]
673
+ d = T[idi]
674
+ col = gr.update(choices=op_columna(idi), label=d["col"])
675
+ fue = gr.update(choices=op_fuerza(idi), value=8.0, label=d["fuerza"])
676
+ return (gr.update(value=d["titulo"]),
677
+ col, gr.update(label=d["capa"]),
678
+ gr.update(label=d["sem"]), fue,
679
+ gr.update(choices=op_tipo(idi), value="gen",
680
+ label=d["tipo"]),
681
+ gr.update(value=d["btn1"]), gr.update(value=d["btn3"]),
682
+ gr.update(label=d["figs"]),
683
+ gr.update(label=d["figb"]),
684
+ gr.update(headers=d["cab"], label=d["porcabeza"]),
685
+ col, gr.update(label=d["capa"]),
686
+ gr.update(label=d["sem"]), fue,
687
+ gr.update(value=d["btn2"]),
688
+ gr.update(label=d["fig"]),
689
+ gr.update(value=QSM[idi]),
690
+ gr.update(label=d["tab1"]), gr.update(label=d["tab2"]),
691
+ gr.update(label=d["tab3"]))
692
+
693
+
694
+ TITULO = "The value-output gauge orbit - La órbita de gauge "\
695
+ "valor-salida"
696
+
697
+ with gr.Blocks(title=TITULO) as demo:
698
+ idi = gr.Radio(list(IDIOMAS), value="English", label="Idioma / Language")
699
+ cab = gr.Markdown(T["en"]["titulo"], latex_delimiters=LATEX)
700
+ with gr.Tab(T["en"]["tab1"]) as pes1:
701
+ with gr.Row():
702
+ col_o = gr.Dropdown(op_columna("en"), value="vitb",
703
+ label=T["en"]["col"])
704
+ capa_o = gr.Dropdown(list(range(12)), value=5,
705
+ label=T["en"]["capa"])
706
+ sem_o = gr.Number(value=0, precision=0, label=T["en"]["sem"])
707
+ fue_o = gr.Dropdown(op_fuerza("en"), value=8.0,
708
+ label=T["en"]["fuerza"])
709
+ tip_o = gr.Radio(op_tipo("en"), value="gen",
710
+ label=T["en"]["tipo"])
711
+ with gr.Row():
712
+ btn_o = gr.Button(T["en"]["btn1"], variant="primary")
713
+ btn_s = gr.Button(T["en"]["btn3"])
714
+ # el barrido va pegado a su botón y en un desplegable que se
715
+ # abre solo: con la salida al final de la pestaña, debajo de
716
+ # la tabla por cabeza, quedaba fuera de pantalla y el botón
717
+ # parecía no hacer nada
718
+ with gr.Accordion(T["en"]["figs"], open=False) as ple_s:
719
+ fig_s = gr.Plot(show_label=False)
720
+ res_o = gr.Markdown(latex_delimiters=LATEX)
721
+ fig_o = gr.Plot(label=T["en"]["figb"])
722
+ tab_o = gr.Dataframe(headers=T["en"]["cab"],
723
+ label=T["en"]["porcabeza"])
724
+ col_o.change(capas_de, col_o, capa_o)
725
+ btn_o.click(orbita, [idi, col_o, capa_o, sem_o, fue_o, tip_o],
726
+ [res_o, tab_o, fig_o])
727
+ btn_s.click(barrido, [idi, col_o, capa_o, sem_o], fig_s).then(
728
+ lambda: gr.update(open=True), None, ple_s)
729
+ with gr.Tab(T["en"]["tab2"]) as pes2:
730
+ with gr.Row():
731
+ col_d = gr.Dropdown(op_columna("en"), value="vitb",
732
+ label=T["en"]["col"])
733
+ capa_d = gr.Dropdown(list(range(12)), value=5,
734
+ label=T["en"]["capa"])
735
+ sem_d = gr.Number(value=0, precision=0, label=T["en"]["sem"])
736
+ fue_d = gr.Dropdown(op_fuerza("en"), value=8.0,
737
+ label=T["en"]["fuerza"])
738
+ btn_d = gr.Button(T["en"]["btn2"], variant="primary")
739
+ res_d = gr.Markdown(latex_delimiters=LATEX)
740
+ fig_m = gr.Plot(label=T["en"]["fig"])
741
+ est = gr.State({})
742
+ col_d.change(capas_de, col_d, capa_d)
743
+ btn_d.click(decision, [idi, col_d, capa_d, sem_d, fue_d, est],
744
+ [res_d, est, fig_m])
745
+ with gr.Tab(T["en"]["tab3"]) as pes3:
746
+ qsm = gr.Markdown(QSM["en"], latex_delimiters=LATEX)
747
+ idi.change(cambia_idioma, idi,
748
+ [cab, col_o, capa_o, sem_o, fue_o, tip_o, btn_o, btn_s,
749
+ ple_s, fig_o, tab_o, col_d, capa_d, sem_d, fue_d,
750
+ btn_d, fig_m, qsm, pes1, pes2, pes3])
751
+
752
+ if __name__ == "__main__":
753
+ demo.launch()
artifacts/demo/sector_vo_pythia.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:65ce0d4a55d05a741cdc3333736cc106b10fc2382d7048ca823bd612b62c8d59
3
+ size 201518424
artifacts/demo/sector_vo_vitb.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c17db69cdd7813d98b126767c8836e9b58cc51e8ff70ae302d8a89d653f05823
3
+ size 56693928
datos.json DELETED
@@ -1,1226 +0,0 @@
1
- {
2
- "L0_suave_s1": {
3
- "img": "imgs/L0_suave_s1.png",
4
- "deriva_v1": 0.6786,
5
- "deriva_circ": 2.4737257953593404e-14,
6
- "ordenes_magnitud": 13,
7
- "par_antes": [
8
- 6,
9
- 7,
10
- 0.882
11
- ],
12
- "par_despues": [
13
- 4,
14
- 10,
15
- 0.23
16
- ],
17
- "mismo_par": false
18
- },
19
- "L0_suave_s2": {
20
- "img": "imgs/L0_suave_s2.png",
21
- "deriva_v1": 0.7727,
22
- "deriva_circ": 4.9862848498447405e-14,
23
- "ordenes_magnitud": 13,
24
- "par_antes": [
25
- 6,
26
- 7,
27
- 0.882
28
- ],
29
- "par_despues": [
30
- 0,
31
- 7,
32
- 0.23
33
- ],
34
- "mismo_par": false
35
- },
36
- "L0_media_s1": {
37
- "img": "imgs/L0_media_s1.png",
38
- "deriva_v1": 0.6294,
39
- "deriva_circ": 1.552756834466925e-14,
40
- "ordenes_magnitud": 13,
41
- "par_antes": [
42
- 6,
43
- 7,
44
- 0.882
45
- ],
46
- "par_despues": [
47
- 5,
48
- 6,
49
- 0.21
50
- ],
51
- "mismo_par": false
52
- },
53
- "L0_media_s2": {
54
- "img": "imgs/L0_media_s2.png",
55
- "deriva_v1": 0.7734,
56
- "deriva_circ": 6.469941050785416e-14,
57
- "ordenes_magnitud": 13,
58
- "par_antes": [
59
- 6,
60
- 7,
61
- 0.882
62
- ],
63
- "par_despues": [
64
- 1,
65
- 6,
66
- 0.217
67
- ],
68
- "mismo_par": false
69
- },
70
- "L0_fuerte_s1": {
71
- "img": "imgs/L0_fuerte_s1.png",
72
- "deriva_v1": 0.6268,
73
- "deriva_circ": 2.6460257025054178e-14,
74
- "ordenes_magnitud": 13,
75
- "par_antes": [
76
- 6,
77
- 7,
78
- 0.882
79
- ],
80
- "par_despues": [
81
- 5,
82
- 6,
83
- 0.16
84
- ],
85
- "mismo_par": false
86
- },
87
- "L0_fuerte_s2": {
88
- "img": "imgs/L0_fuerte_s2.png",
89
- "deriva_v1": 0.787,
90
- "deriva_circ": 2.133179670784439e-14,
91
- "ordenes_magnitud": 13,
92
- "par_antes": [
93
- 6,
94
- 7,
95
- 0.882
96
- ],
97
- "par_despues": [
98
- 1,
99
- 6,
100
- 0.251
101
- ],
102
- "mismo_par": false
103
- },
104
- "L1_suave_s1": {
105
- "img": "imgs/L1_suave_s1.png",
106
- "deriva_v1": 0.7452,
107
- "deriva_circ": 2.99425601847311e-14,
108
- "ordenes_magnitud": 13,
109
- "par_antes": [
110
- 2,
111
- 8,
112
- 0.548
113
- ],
114
- "par_despues": [
115
- 1,
116
- 8,
117
- 0.172
118
- ],
119
- "mismo_par": false
120
- },
121
- "L1_suave_s2": {
122
- "img": "imgs/L1_suave_s2.png",
123
- "deriva_v1": 0.8741,
124
- "deriva_circ": 5.601183727364095e-14,
125
- "ordenes_magnitud": 13,
126
- "par_antes": [
127
- 2,
128
- 8,
129
- 0.548
130
- ],
131
- "par_despues": [
132
- 2,
133
- 8,
134
- 0.116
135
- ],
136
- "mismo_par": true
137
- },
138
- "L1_media_s1": {
139
- "img": "imgs/L1_media_s1.png",
140
- "deriva_v1": 0.8091,
141
- "deriva_circ": 1.80701113271146e-14,
142
- "ordenes_magnitud": 13,
143
- "par_antes": [
144
- 2,
145
- 8,
146
- 0.548
147
- ],
148
- "par_despues": [
149
- 4,
150
- 5,
151
- 0.11
152
- ],
153
- "mismo_par": false
154
- },
155
- "L1_media_s2": {
156
- "img": "imgs/L1_media_s2.png",
157
- "deriva_v1": 0.7987,
158
- "deriva_circ": 7.415678744979554e-14,
159
- "ordenes_magnitud": 13,
160
- "par_antes": [
161
- 2,
162
- 8,
163
- 0.548
164
- ],
165
- "par_despues": [
166
- 5,
167
- 11,
168
- 0.154
169
- ],
170
- "mismo_par": false
171
- },
172
- "L1_fuerte_s1": {
173
- "img": "imgs/L1_fuerte_s1.png",
174
- "deriva_v1": 0.8351,
175
- "deriva_circ": 2.963954738895629e-14,
176
- "ordenes_magnitud": 13,
177
- "par_antes": [
178
- 2,
179
- 8,
180
- 0.548
181
- ],
182
- "par_despues": [
183
- 5,
184
- 7,
185
- 0.122
186
- ],
187
- "mismo_par": false
188
- },
189
- "L1_fuerte_s2": {
190
- "img": "imgs/L1_fuerte_s2.png",
191
- "deriva_v1": 0.7861,
192
- "deriva_circ": 2.613230767485379e-14,
193
- "ordenes_magnitud": 13,
194
- "par_antes": [
195
- 2,
196
- 8,
197
- 0.548
198
- ],
199
- "par_despues": [
200
- 5,
201
- 11,
202
- 0.148
203
- ],
204
- "mismo_par": false
205
- },
206
- "L2_suave_s1": {
207
- "img": "imgs/L2_suave_s1.png",
208
- "deriva_v1": 0.8127,
209
- "deriva_circ": 2.879997179974982e-14,
210
- "ordenes_magnitud": 13,
211
- "par_antes": [
212
- 2,
213
- 11,
214
- 0.512
215
- ],
216
- "par_despues": [
217
- 2,
218
- 8,
219
- 0.118
220
- ],
221
- "mismo_par": false
222
- },
223
- "L2_suave_s2": {
224
- "img": "imgs/L2_suave_s2.png",
225
- "deriva_v1": 0.8315,
226
- "deriva_circ": 5.764363813108119e-14,
227
- "ordenes_magnitud": 13,
228
- "par_antes": [
229
- 2,
230
- 11,
231
- 0.512
232
- ],
233
- "par_despues": [
234
- 2,
235
- 11,
236
- 0.158
237
- ],
238
- "mismo_par": true
239
- },
240
- "L2_media_s1": {
241
- "img": "imgs/L2_media_s1.png",
242
- "deriva_v1": 0.8766,
243
- "deriva_circ": 1.905719006806521e-14,
244
- "ordenes_magnitud": 13,
245
- "par_antes": [
246
- 2,
247
- 11,
248
- 0.512
249
- ],
250
- "par_despues": [
251
- 2,
252
- 11,
253
- 0.193
254
- ],
255
- "mismo_par": true
256
- },
257
- "L2_media_s2": {
258
- "img": "imgs/L2_media_s2.png",
259
- "deriva_v1": 0.8431,
260
- "deriva_circ": 8.191468928587784e-14,
261
- "ordenes_magnitud": 13,
262
- "par_antes": [
263
- 2,
264
- 11,
265
- 0.512
266
- ],
267
- "par_despues": [
268
- 1,
269
- 6,
270
- 0.144
271
- ],
272
- "mismo_par": false
273
- },
274
- "L2_fuerte_s1": {
275
- "img": "imgs/L2_fuerte_s1.png",
276
- "deriva_v1": 0.9084,
277
- "deriva_circ": 3.027170995207917e-14,
278
- "ordenes_magnitud": 13,
279
- "par_antes": [
280
- 2,
281
- 11,
282
- 0.512
283
- ],
284
- "par_despues": [
285
- 2,
286
- 11,
287
- 0.19
288
- ],
289
- "mismo_par": true
290
- },
291
- "L2_fuerte_s2": {
292
- "img": "imgs/L2_fuerte_s2.png",
293
- "deriva_v1": 0.8408,
294
- "deriva_circ": 2.6019565593414515e-14,
295
- "ordenes_magnitud": 13,
296
- "par_antes": [
297
- 2,
298
- 11,
299
- 0.512
300
- ],
301
- "par_despues": [
302
- 1,
303
- 6,
304
- 0.143
305
- ],
306
- "mismo_par": false
307
- },
308
- "L3_suave_s1": {
309
- "img": "imgs/L3_suave_s1.png",
310
- "deriva_v1": 0.8759,
311
- "deriva_circ": 2.8129642052878183e-14,
312
- "ordenes_magnitud": 13,
313
- "par_antes": [
314
- 4,
315
- 9,
316
- 0.171
317
- ],
318
- "par_despues": [
319
- 2,
320
- 9,
321
- 0.113
322
- ],
323
- "mismo_par": false
324
- },
325
- "L3_suave_s2": {
326
- "img": "imgs/L3_suave_s2.png",
327
- "deriva_v1": 0.8475,
328
- "deriva_circ": 5.809119070152899e-14,
329
- "ordenes_magnitud": 13,
330
- "par_antes": [
331
- 4,
332
- 9,
333
- 0.171
334
- ],
335
- "par_despues": [
336
- 3,
337
- 8,
338
- 0.11
339
- ],
340
- "mismo_par": false
341
- },
342
- "L3_media_s1": {
343
- "img": "imgs/L3_media_s1.png",
344
- "deriva_v1": 0.8642,
345
- "deriva_circ": 1.8837829840199678e-14,
346
- "ordenes_magnitud": 13,
347
- "par_antes": [
348
- 4,
349
- 9,
350
- 0.171
351
- ],
352
- "par_despues": [
353
- 0,
354
- 1,
355
- 0.153
356
- ],
357
- "mismo_par": false
358
- },
359
- "L3_media_s2": {
360
- "img": "imgs/L3_media_s2.png",
361
- "deriva_v1": 0.8581,
362
- "deriva_circ": 7.957037835563568e-14,
363
- "ordenes_magnitud": 13,
364
- "par_antes": [
365
- 4,
366
- 9,
367
- 0.171
368
- ],
369
- "par_despues": [
370
- 4,
371
- 11,
372
- 0.124
373
- ],
374
- "mismo_par": false
375
- },
376
- "L3_fuerte_s1": {
377
- "img": "imgs/L3_fuerte_s1.png",
378
- "deriva_v1": 0.8548,
379
- "deriva_circ": 2.95812812652673e-14,
380
- "ordenes_magnitud": 13,
381
- "par_antes": [
382
- 4,
383
- 9,
384
- 0.171
385
- ],
386
- "par_despues": [
387
- 2,
388
- 6,
389
- 0.14
390
- ],
391
- "mismo_par": false
392
- },
393
- "L3_fuerte_s2": {
394
- "img": "imgs/L3_fuerte_s2.png",
395
- "deriva_v1": 0.8444,
396
- "deriva_circ": 2.7124494874277234e-14,
397
- "ordenes_magnitud": 13,
398
- "par_antes": [
399
- 4,
400
- 9,
401
- 0.171
402
- ],
403
- "par_despues": [
404
- 4,
405
- 11,
406
- 0.112
407
- ],
408
- "mismo_par": false
409
- },
410
- "L4_suave_s1": {
411
- "img": "imgs/L4_suave_s1.png",
412
- "deriva_v1": 0.8383,
413
- "deriva_circ": 2.9134602291283086e-14,
414
- "ordenes_magnitud": 13,
415
- "par_antes": [
416
- 2,
417
- 6,
418
- 0.225
419
- ],
420
- "par_despues": [
421
- 5,
422
- 10,
423
- 0.155
424
- ],
425
- "mismo_par": false
426
- },
427
- "L4_suave_s2": {
428
- "img": "imgs/L4_suave_s2.png",
429
- "deriva_v1": 0.8496,
430
- "deriva_circ": 5.825031497296666e-14,
431
- "ordenes_magnitud": 13,
432
- "par_antes": [
433
- 2,
434
- 6,
435
- 0.225
436
- ],
437
- "par_despues": [
438
- 1,
439
- 10,
440
- 0.117
441
- ],
442
- "mismo_par": false
443
- },
444
- "L4_media_s1": {
445
- "img": "imgs/L4_media_s1.png",
446
- "deriva_v1": 0.8302,
447
- "deriva_circ": 1.9144603416035137e-14,
448
- "ordenes_magnitud": 13,
449
- "par_antes": [
450
- 2,
451
- 6,
452
- 0.225
453
- ],
454
- "par_despues": [
455
- 5,
456
- 10,
457
- 0.16
458
- ],
459
- "mismo_par": false
460
- },
461
- "L4_media_s2": {
462
- "img": "imgs/L4_media_s2.png",
463
- "deriva_v1": 0.809,
464
- "deriva_circ": 7.769447875944547e-14,
465
- "ordenes_magnitud": 13,
466
- "par_antes": [
467
- 2,
468
- 6,
469
- 0.225
470
- ],
471
- "par_despues": [
472
- 3,
473
- 10,
474
- 0.098
475
- ],
476
- "mismo_par": false
477
- },
478
- "L4_fuerte_s1": {
479
- "img": "imgs/L4_fuerte_s1.png",
480
- "deriva_v1": 0.8443,
481
- "deriva_circ": 3.015301548006439e-14,
482
- "ordenes_magnitud": 13,
483
- "par_antes": [
484
- 2,
485
- 6,
486
- 0.225
487
- ],
488
- "par_despues": [
489
- 5,
490
- 10,
491
- 0.15
492
- ],
493
- "mismo_par": false
494
- },
495
- "L4_fuerte_s2": {
496
- "img": "imgs/L4_fuerte_s2.png",
497
- "deriva_v1": 0.8071,
498
- "deriva_circ": 2.7054490186557386e-14,
499
- "ordenes_magnitud": 13,
500
- "par_antes": [
501
- 2,
502
- 6,
503
- 0.225
504
- ],
505
- "par_despues": [
506
- 1,
507
- 9,
508
- 0.113
509
- ],
510
- "mismo_par": false
511
- },
512
- "L5_suave_s1": {
513
- "img": "imgs/L5_suave_s1.png",
514
- "deriva_v1": 0.8377,
515
- "deriva_circ": 3.047820941731155e-14,
516
- "ordenes_magnitud": 13,
517
- "par_antes": [
518
- 3,
519
- 9,
520
- 0.454
521
- ],
522
- "par_despues": [
523
- 5,
524
- 7,
525
- 0.189
526
- ],
527
- "mismo_par": false
528
- },
529
- "L5_suave_s2": {
530
- "img": "imgs/L5_suave_s2.png",
531
- "deriva_v1": 0.8498,
532
- "deriva_circ": 5.865610656173236e-14,
533
- "ordenes_magnitud": 13,
534
- "par_antes": [
535
- 3,
536
- 9,
537
- 0.454
538
- ],
539
- "par_despues": [
540
- 3,
541
- 9,
542
- 0.103
543
- ],
544
- "mismo_par": true
545
- },
546
- "L5_media_s1": {
547
- "img": "imgs/L5_media_s1.png",
548
- "deriva_v1": 0.8772,
549
- "deriva_circ": 1.931950503998985e-14,
550
- "ordenes_magnitud": 13,
551
- "par_antes": [
552
- 3,
553
- 9,
554
- 0.454
555
- ],
556
- "par_despues": [
557
- 1,
558
- 9,
559
- 0.135
560
- ],
561
- "mismo_par": false
562
- },
563
- "L5_media_s2": {
564
- "img": "imgs/L5_media_s2.png",
565
- "deriva_v1": 0.8122,
566
- "deriva_circ": 8.236064575967118e-14,
567
- "ordenes_magnitud": 12,
568
- "par_antes": [
569
- 3,
570
- 9,
571
- 0.454
572
- ],
573
- "par_despues": [
574
- 0,
575
- 1,
576
- 0.098
577
- ],
578
- "mismo_par": false
579
- },
580
- "L5_fuerte_s1": {
581
- "img": "imgs/L5_fuerte_s1.png",
582
- "deriva_v1": 0.8701,
583
- "deriva_circ": 3.0786391199404436e-14,
584
- "ordenes_magnitud": 13,
585
- "par_antes": [
586
- 3,
587
- 9,
588
- 0.454
589
- ],
590
- "par_despues": [
591
- 1,
592
- 9,
593
- 0.121
594
- ],
595
- "mismo_par": false
596
- },
597
- "L5_fuerte_s2": {
598
- "img": "imgs/L5_fuerte_s2.png",
599
- "deriva_v1": 0.7986,
600
- "deriva_circ": 2.7028471609653922e-14,
601
- "ordenes_magnitud": 13,
602
- "par_antes": [
603
- 3,
604
- 9,
605
- 0.454
606
- ],
607
- "par_despues": [
608
- 0,
609
- 1,
610
- 0.106
611
- ],
612
- "mismo_par": false
613
- },
614
- "L6_suave_s1": {
615
- "img": "imgs/L6_suave_s1.png",
616
- "deriva_v1": 0.8506,
617
- "deriva_circ": 3.162705240413337e-14,
618
- "ordenes_magnitud": 13,
619
- "par_antes": [
620
- 6,
621
- 8,
622
- 0.703
623
- ],
624
- "par_despues": [
625
- 5,
626
- 9,
627
- 0.13
628
- ],
629
- "mismo_par": false
630
- },
631
- "L6_suave_s2": {
632
- "img": "imgs/L6_suave_s2.png",
633
- "deriva_v1": 0.8241,
634
- "deriva_circ": 5.797686128711883e-14,
635
- "ordenes_magnitud": 13,
636
- "par_antes": [
637
- 6,
638
- 8,
639
- 0.703
640
- ],
641
- "par_despues": [
642
- 10,
643
- 11,
644
- 0.208
645
- ],
646
- "mismo_par": false
647
- },
648
- "L6_media_s1": {
649
- "img": "imgs/L6_media_s1.png",
650
- "deriva_v1": 0.8474,
651
- "deriva_circ": 1.9306993491334054e-14,
652
- "ordenes_magnitud": 13,
653
- "par_antes": [
654
- 6,
655
- 8,
656
- 0.703
657
- ],
658
- "par_despues": [
659
- 2,
660
- 6,
661
- 0.139
662
- ],
663
- "mismo_par": false
664
- },
665
- "L6_media_s2": {
666
- "img": "imgs/L6_media_s2.png",
667
- "deriva_v1": 0.8537,
668
- "deriva_circ": 7.479571097365316e-14,
669
- "ordenes_magnitud": 13,
670
- "par_antes": [
671
- 6,
672
- 8,
673
- 0.703
674
- ],
675
- "par_despues": [
676
- 2,
677
- 6,
678
- 0.132
679
- ],
680
- "mismo_par": false
681
- },
682
- "L6_fuerte_s1": {
683
- "img": "imgs/L6_fuerte_s1.png",
684
- "deriva_v1": 0.8259,
685
- "deriva_circ": 2.9529236640449954e-14,
686
- "ordenes_magnitud": 13,
687
- "par_antes": [
688
- 6,
689
- 8,
690
- 0.703
691
- ],
692
- "par_despues": [
693
- 2,
694
- 6,
695
- 0.17
696
- ],
697
- "mismo_par": false
698
- },
699
- "L6_fuerte_s2": {
700
- "img": "imgs/L6_fuerte_s2.png",
701
- "deriva_v1": 0.8447,
702
- "deriva_circ": 2.7626367797404217e-14,
703
- "ordenes_magnitud": 13,
704
- "par_antes": [
705
- 6,
706
- 8,
707
- 0.703
708
- ],
709
- "par_despues": [
710
- 2,
711
- 6,
712
- 0.113
713
- ],
714
- "mismo_par": false
715
- },
716
- "L7_suave_s1": {
717
- "img": "imgs/L7_suave_s1.png",
718
- "deriva_v1": 0.8616,
719
- "deriva_circ": 3.1011565115169496e-14,
720
- "ordenes_magnitud": 13,
721
- "par_antes": [
722
- 5,
723
- 10,
724
- 0.647
725
- ],
726
- "par_despues": [
727
- 9,
728
- 10,
729
- 0.101
730
- ],
731
- "mismo_par": false
732
- },
733
- "L7_suave_s2": {
734
- "img": "imgs/L7_suave_s2.png",
735
- "deriva_v1": 0.8613,
736
- "deriva_circ": 5.761667178513975e-14,
737
- "ordenes_magnitud": 13,
738
- "par_antes": [
739
- 5,
740
- 10,
741
- 0.647
742
- ],
743
- "par_despues": [
744
- 3,
745
- 5,
746
- 0.112
747
- ],
748
- "mismo_par": false
749
- },
750
- "L7_media_s1": {
751
- "img": "imgs/L7_media_s1.png",
752
- "deriva_v1": 0.8736,
753
- "deriva_circ": 1.9817014947860575e-14,
754
- "ordenes_magnitud": 13,
755
- "par_antes": [
756
- 5,
757
- 10,
758
- 0.647
759
- ],
760
- "par_despues": [
761
- 5,
762
- 8,
763
- 0.127
764
- ],
765
- "mismo_par": false
766
- },
767
- "L7_media_s2": {
768
- "img": "imgs/L7_media_s2.png",
769
- "deriva_v1": 0.8387,
770
- "deriva_circ": 8.280716335893669e-14,
771
- "ordenes_magnitud": 13,
772
- "par_antes": [
773
- 5,
774
- 10,
775
- 0.647
776
- ],
777
- "par_despues": [
778
- 1,
779
- 7,
780
- 0.088
781
- ],
782
- "mismo_par": false
783
- },
784
- "L7_fuerte_s1": {
785
- "img": "imgs/L7_fuerte_s1.png",
786
- "deriva_v1": 0.8789,
787
- "deriva_circ": 3.1076818388437473e-14,
788
- "ordenes_magnitud": 13,
789
- "par_antes": [
790
- 5,
791
- 10,
792
- 0.647
793
- ],
794
- "par_despues": [
795
- 5,
796
- 8,
797
- 0.113
798
- ],
799
- "mismo_par": false
800
- },
801
- "L7_fuerte_s2": {
802
- "img": "imgs/L7_fuerte_s2.png",
803
- "deriva_v1": 0.8362,
804
- "deriva_circ": 2.794673517007365e-14,
805
- "ordenes_magnitud": 13,
806
- "par_antes": [
807
- 5,
808
- 10,
809
- 0.647
810
- ],
811
- "par_despues": [
812
- 8,
813
- 10,
814
- 0.07
815
- ],
816
- "mismo_par": false
817
- },
818
- "L8_suave_s1": {
819
- "img": "imgs/L8_suave_s1.png",
820
- "deriva_v1": 0.8371,
821
- "deriva_circ": 3.159660707976963e-14,
822
- "ordenes_magnitud": 13,
823
- "par_antes": [
824
- 6,
825
- 8,
826
- 0.923
827
- ],
828
- "par_despues": [
829
- 3,
830
- 6,
831
- 0.106
832
- ],
833
- "mismo_par": false
834
- },
835
- "L8_suave_s2": {
836
- "img": "imgs/L8_suave_s2.png",
837
- "deriva_v1": 0.8326,
838
- "deriva_circ": 6.179127019580005e-14,
839
- "ordenes_magnitud": 13,
840
- "par_antes": [
841
- 6,
842
- 8,
843
- 0.923
844
- ],
845
- "par_despues": [
846
- 5,
847
- 6,
848
- 0.087
849
- ],
850
- "mismo_par": false
851
- },
852
- "L8_media_s1": {
853
- "img": "imgs/L8_media_s1.png",
854
- "deriva_v1": 0.8297,
855
- "deriva_circ": 1.9967340691735304e-14,
856
- "ordenes_magnitud": 13,
857
- "par_antes": [
858
- 6,
859
- 8,
860
- 0.923
861
- ],
862
- "par_despues": [
863
- 6,
864
- 8,
865
- 0.119
866
- ],
867
- "mismo_par": true
868
- },
869
- "L8_media_s2": {
870
- "img": "imgs/L8_media_s2.png",
871
- "deriva_v1": 0.8221,
872
- "deriva_circ": 8.406723915372021e-14,
873
- "ordenes_magnitud": 12,
874
- "par_antes": [
875
- 6,
876
- 8,
877
- 0.923
878
- ],
879
- "par_despues": [
880
- 3,
881
- 7,
882
- 0.091
883
- ],
884
- "mismo_par": false
885
- },
886
- "L8_fuerte_s1": {
887
- "img": "imgs/L8_fuerte_s1.png",
888
- "deriva_v1": 0.844,
889
- "deriva_circ": 3.1070542356021955e-14,
890
- "ordenes_magnitud": 13,
891
- "par_antes": [
892
- 6,
893
- 8,
894
- 0.923
895
- ],
896
- "par_despues": [
897
- 2,
898
- 8,
899
- 0.113
900
- ],
901
- "mismo_par": false
902
- },
903
- "L8_fuerte_s2": {
904
- "img": "imgs/L8_fuerte_s2.png",
905
- "deriva_v1": 0.827,
906
- "deriva_circ": 2.8388147017913648e-14,
907
- "ordenes_magnitud": 13,
908
- "par_antes": [
909
- 6,
910
- 8,
911
- 0.923
912
- ],
913
- "par_despues": [
914
- 8,
915
- 11,
916
- 0.092
917
- ],
918
- "mismo_par": false
919
- },
920
- "L9_suave_s1": {
921
- "img": "imgs/L9_suave_s1.png",
922
- "deriva_v1": 0.91,
923
- "deriva_circ": 3.113742104090125e-14,
924
- "ordenes_magnitud": 13,
925
- "par_antes": [
926
- 0,
927
- 8,
928
- 0.422
929
- ],
930
- "par_despues": [
931
- 0,
932
- 8,
933
- 0.109
934
- ],
935
- "mismo_par": true
936
- },
937
- "L9_suave_s2": {
938
- "img": "imgs/L9_suave_s2.png",
939
- "deriva_v1": 0.877,
940
- "deriva_circ": 6.094876258725251e-14,
941
- "ordenes_magnitud": 13,
942
- "par_antes": [
943
- 0,
944
- 8,
945
- 0.422
946
- ],
947
- "par_despues": [
948
- 3,
949
- 4,
950
- 0.104
951
- ],
952
- "mismo_par": false
953
- },
954
- "L9_media_s1": {
955
- "img": "imgs/L9_media_s1.png",
956
- "deriva_v1": 0.933,
957
- "deriva_circ": 2.007609235255005e-14,
958
- "ordenes_magnitud": 13,
959
- "par_antes": [
960
- 0,
961
- 8,
962
- 0.422
963
- ],
964
- "par_despues": [
965
- 3,
966
- 5,
967
- 0.074
968
- ],
969
- "mismo_par": false
970
- },
971
- "L9_media_s2": {
972
- "img": "imgs/L9_media_s2.png",
973
- "deriva_v1": 0.8425,
974
- "deriva_circ": 8.324532178188378e-14,
975
- "ordenes_magnitud": 13,
976
- "par_antes": [
977
- 0,
978
- 8,
979
- 0.422
980
- ],
981
- "par_despues": [
982
- 3,
983
- 10,
984
- 0.121
985
- ],
986
- "mismo_par": false
987
- },
988
- "L9_fuerte_s1": {
989
- "img": "imgs/L9_fuerte_s1.png",
990
- "deriva_v1": 0.9228,
991
- "deriva_circ": 3.17377462724315e-14,
992
- "ordenes_magnitud": 13,
993
- "par_antes": [
994
- 0,
995
- 8,
996
- 0.422
997
- ],
998
- "par_despues": [
999
- 5,
1000
- 9,
1001
- 0.081
1002
- ],
1003
- "mismo_par": false
1004
- },
1005
- "L9_fuerte_s2": {
1006
- "img": "imgs/L9_fuerte_s2.png",
1007
- "deriva_v1": 0.8389,
1008
- "deriva_circ": 2.8617318086734605e-14,
1009
- "ordenes_magnitud": 13,
1010
- "par_antes": [
1011
- 0,
1012
- 8,
1013
- 0.422
1014
- ],
1015
- "par_despues": [
1016
- 3,
1017
- 10,
1018
- 0.109
1019
- ],
1020
- "mismo_par": false
1021
- },
1022
- "L10_suave_s1": {
1023
- "img": "imgs/L10_suave_s1.png",
1024
- "deriva_v1": 0.8477,
1025
- "deriva_circ": 3.110335961284467e-14,
1026
- "ordenes_magnitud": 13,
1027
- "par_antes": [
1028
- 0,
1029
- 3,
1030
- 0.583
1031
- ],
1032
- "par_despues": [
1033
- 7,
1034
- 10,
1035
- 0.105
1036
- ],
1037
- "mismo_par": false
1038
- },
1039
- "L10_suave_s2": {
1040
- "img": "imgs/L10_suave_s2.png",
1041
- "deriva_v1": 0.8731,
1042
- "deriva_circ": 6.105995368288468e-14,
1043
- "ordenes_magnitud": 13,
1044
- "par_antes": [
1045
- 0,
1046
- 3,
1047
- 0.583
1048
- ],
1049
- "par_despues": [
1050
- 0,
1051
- 7,
1052
- 0.097
1053
- ],
1054
- "mismo_par": false
1055
- },
1056
- "L10_media_s1": {
1057
- "img": "imgs/L10_media_s1.png",
1058
- "deriva_v1": 0.8662,
1059
- "deriva_circ": 1.9916351714317566e-14,
1060
- "ordenes_magnitud": 13,
1061
- "par_antes": [
1062
- 0,
1063
- 3,
1064
- 0.583
1065
- ],
1066
- "par_despues": [
1067
- 7,
1068
- 8,
1069
- 0.108
1070
- ],
1071
- "mismo_par": false
1072
- },
1073
- "L10_media_s2": {
1074
- "img": "imgs/L10_media_s2.png",
1075
- "deriva_v1": 0.8814,
1076
- "deriva_circ": 8.313739099757465e-14,
1077
- "ordenes_magnitud": 13,
1078
- "par_antes": [
1079
- 0,
1080
- 3,
1081
- 0.583
1082
- ],
1083
- "par_despues": [
1084
- 0,
1085
- 7,
1086
- 0.088
1087
- ],
1088
- "mismo_par": false
1089
- },
1090
- "L10_fuerte_s1": {
1091
- "img": "imgs/L10_fuerte_s1.png",
1092
- "deriva_v1": 0.8551,
1093
- "deriva_circ": 3.12802461650481e-14,
1094
- "ordenes_magnitud": 13,
1095
- "par_antes": [
1096
- 0,
1097
- 3,
1098
- 0.583
1099
- ],
1100
- "par_despues": [
1101
- 7,
1102
- 8,
1103
- 0.095
1104
- ],
1105
- "mismo_par": false
1106
- },
1107
- "L10_fuerte_s2": {
1108
- "img": "imgs/L10_fuerte_s2.png",
1109
- "deriva_v1": 0.8863,
1110
- "deriva_circ": 2.869690316839748e-14,
1111
- "ordenes_magnitud": 13,
1112
- "par_antes": [
1113
- 0,
1114
- 3,
1115
- 0.583
1116
- ],
1117
- "par_despues": [
1118
- 1,
1119
- 10,
1120
- 0.084
1121
- ],
1122
- "mismo_par": false
1123
- },
1124
- "L11_suave_s1": {
1125
- "img": "imgs/L11_suave_s1.png",
1126
- "deriva_v1": 0.7919,
1127
- "deriva_circ": 2.878703791209282e-14,
1128
- "ordenes_magnitud": 13,
1129
- "par_antes": [
1130
- 8,
1131
- 9,
1132
- 0.92
1133
- ],
1134
- "par_despues": [
1135
- 3,
1136
- 7,
1137
- 0.176
1138
- ],
1139
- "mismo_par": false
1140
- },
1141
- "L11_suave_s2": {
1142
- "img": "imgs/L11_suave_s2.png",
1143
- "deriva_v1": 0.8022,
1144
- "deriva_circ": 6.204952614598513e-14,
1145
- "ordenes_magnitud": 13,
1146
- "par_antes": [
1147
- 8,
1148
- 9,
1149
- 0.92
1150
- ],
1151
- "par_despues": [
1152
- 4,
1153
- 9,
1154
- 0.237
1155
- ],
1156
- "mismo_par": false
1157
- },
1158
- "L11_media_s1": {
1159
- "img": "imgs/L11_media_s1.png",
1160
- "deriva_v1": 0.7892,
1161
- "deriva_circ": 1.984885674138256e-14,
1162
- "ordenes_magnitud": 13,
1163
- "par_antes": [
1164
- 8,
1165
- 9,
1166
- 0.92
1167
- ],
1168
- "par_despues": [
1169
- 3,
1170
- 7,
1171
- 0.185
1172
- ],
1173
- "mismo_par": false
1174
- },
1175
- "L11_media_s2": {
1176
- "img": "imgs/L11_media_s2.png",
1177
- "deriva_v1": 0.8032,
1178
- "deriva_circ": 8.129645132437228e-14,
1179
- "ordenes_magnitud": 12,
1180
- "par_antes": [
1181
- 8,
1182
- 9,
1183
- 0.92
1184
- ],
1185
- "par_despues": [
1186
- 4,
1187
- 9,
1188
- 0.22
1189
- ],
1190
- "mismo_par": false
1191
- },
1192
- "L11_fuerte_s1": {
1193
- "img": "imgs/L11_fuerte_s1.png",
1194
- "deriva_v1": 0.7973,
1195
- "deriva_circ": 3.1385631552222743e-14,
1196
- "ordenes_magnitud": 13,
1197
- "par_antes": [
1198
- 8,
1199
- 9,
1200
- 0.92
1201
- ],
1202
- "par_despues": [
1203
- 3,
1204
- 7,
1205
- 0.181
1206
- ],
1207
- "mismo_par": false
1208
- },
1209
- "L11_fuerte_s2": {
1210
- "img": "imgs/L11_fuerte_s2.png",
1211
- "deriva_v1": 0.8202,
1212
- "deriva_circ": 2.807503880189621e-14,
1213
- "ordenes_magnitud": 13,
1214
- "par_antes": [
1215
- 8,
1216
- 9,
1217
- 0.92
1218
- ],
1219
- "par_despues": [
1220
- 4,
1221
- 9,
1222
- 0.178
1223
- ],
1224
- "mismo_par": false
1225
- }
1226
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
demo/portador.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """portador ligero del sector valor-salida para la demo.
2
+
3
+ `src/gauge_flip.py` opera sobre `modelo.blocks[capa].attn.qkv` y
4
+ `.proj`, es decir sobre un vit de timm de 984 mb. la demo no puede
5
+ cargar eso, pero tampoco debe reimplementar el gauge: reimplementarlo
6
+ sería perder la propiedad que hace fiable a esta ruta ---que la demo
7
+ ejecuta el mismo código que produjo las tablas 2 y 3---.
8
+
9
+ la salida es un objeto mínimo que expone exactamente esa interfaz
10
+ sobre los tensores del safetensors. `aplica_gauge_ov` corre **sin una
11
+ sola modificación**; solo toca las filas de valor del qkv, su sesgo y
12
+ las columnas de la proyección de salida, que es lo que el portador
13
+ tiene. el resto del qkv es relleno que la función jamás lee.
14
+ """
15
+
16
+ import pathlib
17
+
18
+ import torch
19
+ from safetensors.torch import load_file
20
+
21
+
22
+ class _Lineal:
23
+ """imita `nn.Linear` en los dos atributos que el gauge usa."""
24
+
25
+ def __init__(self, weight: torch.Tensor,
26
+ bias: torch.Tensor | None = None) -> None:
27
+ """guarda peso y sesgo como tensores mutables.
28
+
29
+ Args:
30
+ weight: matriz de pesos.
31
+ bias: vector de sesgo, o None.
32
+ """
33
+ self.weight = weight
34
+ self.bias = bias
35
+
36
+
37
+ class _Atencion:
38
+ """imita el módulo de atención de timm: `.qkv` y `.proj`."""
39
+
40
+ def __init__(self, qkv: _Lineal, proj: _Lineal) -> None:
41
+ """agrupa las dos proyecciones.
42
+
43
+ Args:
44
+ qkv: proyección fusionada consulta-clave-valor.
45
+ proj: proyección de salida.
46
+ """
47
+ self.qkv = qkv
48
+ self.proj = proj
49
+
50
+
51
+ class _Bloque:
52
+ """imita un bloque de timm: solo expone `.attn`."""
53
+
54
+ def __init__(self, attn: _Atencion) -> None:
55
+ """envuelve la atención.
56
+
57
+ Args:
58
+ attn: el módulo de atención del bloque.
59
+ """
60
+ self.attn = attn
61
+
62
+
63
+ class PortadorVO:
64
+ """vit de mentira con el sector valor-salida de verdad.
65
+
66
+ Attributes:
67
+ blocks: lista de bloques con la interfaz que el gauge espera.
68
+ n_cabezas: cabezas por capa.
69
+ dim_cabeza: d_h.
70
+ """
71
+
72
+ def __init__(self, ruta: str) -> None:
73
+ """monta el portador desde el safetensors del sector.
74
+
75
+ los tensores se elevan a float64. `aplica_gauge_ov` calcula r y
76
+ su inversa en doble y devuelve el resultado al dtype del peso:
77
+ con el portador en simple, ese último casteo deja un error de
78
+ ~1e-7 relativo y el circuito ov ---que es exactamente
79
+ invariante--- aparece moviéndose milésimas de grado, más que
80
+ v1(w_o) bajo un gauge ortogonal. en doble no se pierde nada y
81
+ la invariancia se lee al orden que certifica la fase g.
82
+
83
+ Args:
84
+ ruta: fichero generado por `scripts/extraer_sector_vo.py`.
85
+ """
86
+ s = load_file(ruta)
87
+ capas = 1 + max(int(k.split(".")[0][1:]) for k in s)
88
+ self.n_cabezas = 1 + max(int(k.split(".")[1][1:]) for k in s)
89
+ self.dim_cabeza = s["L0.h0.w_v"].shape[0]
90
+ d = s["L0.h0.w_v"].shape[1]
91
+ nh, dh = self.n_cabezas, self.dim_cabeza
92
+ self.blocks = []
93
+ for c in range(capas):
94
+ qkv_w = torch.zeros(3 * d, d, dtype=torch.float64)
95
+ qkv_b = torch.zeros(3 * d, dtype=torch.float64)
96
+ proj_w = torch.zeros(d, d, dtype=torch.float64)
97
+ for h in range(nh):
98
+ fil = slice(2 * d + h * dh, 2 * d + (h + 1) * dh)
99
+ col = slice(h * dh, (h + 1) * dh)
100
+ qkv_w[fil, :] = s[f"L{c}.h{h}.w_v"].double()
101
+ qkv_b[fil] = s[f"L{c}.h{h}.b_v"].double()
102
+ proj_w[:, col] = (
103
+ s[f"L{c}.h{h}.w_o"].t().double())
104
+ self.blocks.append(
105
+ _Bloque(_Atencion(_Lineal(qkv_w, qkv_b), _Lineal(proj_w))))
106
+
107
+ def w_o_por_cabeza(self, capa: int) -> torch.Tensor:
108
+ """proyección de salida por cabeza, tras el gauge que haya.
109
+
110
+ Args:
111
+ capa: índice de capa.
112
+
113
+ Returns:
114
+ tensor [h, dh, d].
115
+ """
116
+ w = self.blocks[capa].attn.proj.weight
117
+ dh = self.dim_cabeza
118
+ return torch.stack([w[:, h * dh:(h + 1) * dh].t()
119
+ for h in range(self.n_cabezas)])
120
+
121
+ def copia(self) -> "PortadorVO":
122
+ """duplica el portador para aplicarle un gauge sin destruirlo.
123
+
124
+ Returns:
125
+ un portador independiente con los mismos pesos.
126
+ """
127
+ otro = PortadorVO.__new__(PortadorVO)
128
+ otro.n_cabezas, otro.dim_cabeza = self.n_cabezas, self.dim_cabeza
129
+ otro.blocks = []
130
+ for b in self.blocks:
131
+ q, p = b.attn.qkv, b.attn.proj
132
+ otro.blocks.append(_Bloque(_Atencion(
133
+ _Lineal(q.weight.clone(), q.bias.clone()),
134
+ _Lineal(p.weight.clone()))))
135
+ return otro
136
+
137
+
138
+ def carga(col: str = "vitb", raiz: str = "artifacts/demo") -> PortadorVO:
139
+ """carga el portador de una columna.
140
+
141
+ Args:
142
+ col: etiqueta de columna (vitb o pythia).
143
+ raiz: directorio de los safetensors.
144
+
145
+ Returns:
146
+ el portador montado.
147
+
148
+ Raises:
149
+ FileNotFoundError: si falta el fichero del sector.
150
+ """
151
+ ruta = pathlib.Path(raiz) / f"sector_vo_{col}.safetensors"
152
+ if not ruta.exists():
153
+ raise FileNotFoundError(
154
+ f"{ruta}: ejecuta antes scripts/extraer_sector_vo.py")
155
+ return PortadorVO(str(ruta))
156
+
157
+
158
+ def verifica_relleno(p: PortadorVO, capa: int = 0,
159
+ escala_id: float = 8.0) -> None:
160
+ """comprueba que el gauge no toca las regiones de relleno.
161
+
162
+ el portador funciona porque `aplica_gauge_ov` solo lee y escribe
163
+ las filas de valor del qkv, su sesgo y las columnas de la
164
+ proyección de salida. eso es un contrato implícito con `src/`: si
165
+ una versión futura del gauge tocara consulta o clave, el portador
166
+ serviría derivas de un modelo que ya no es el del paper, y lo
167
+ haría en silencio. este assert convierte el contrato en fallo
168
+ ruidoso.
169
+
170
+ Args:
171
+ p: portador ya montado.
172
+ capa: capa sobre la que probar.
173
+ escala_id: fuerza del gauge de prueba.
174
+
175
+ Raises:
176
+ AssertionError: si el gauge escribe fuera del sector de valor.
177
+ """
178
+ from src.gauge_flip import aplica_gauge_ov
179
+
180
+ q = p.copia()
181
+ w = q.blocks[capa].attn.qkv.weight
182
+ d = w.shape[1]
183
+ antes_w = w[:2 * d, :].clone()
184
+ antes_b = q.blocks[capa].attn.qkv.bias[:2 * d].clone()
185
+ aplica_gauge_ov(q, capa, q.n_cabezas, q.dim_cabeza, semilla=0,
186
+ escala_id=escala_id)
187
+ assert torch.equal(q.blocks[capa].attn.qkv.weight[:2 * d, :], antes_w), (
188
+ "el gauge escribió en las filas de consulta o clave: el portador "
189
+ "ya no representa al modelo del paper")
190
+ assert torch.equal(q.blocks[capa].attn.qkv.bias[:2 * d], antes_b), (
191
+ "el gauge escribió en el sesgo de consulta o clave")
192
+
193
+
194
+ def verifica_manifiesto(raiz: str = ".") -> None:
195
+ """contrasta el sha256 de lo vendido contra `manifiesto.sha256`.
196
+
197
+ el Space lleva copias del código del paper, y las copias derivan.
198
+ esto es el patrón de identidad byte a byte de g4 aplicado al
199
+ código: si un fichero cambió, el arranque falla en vez de servir
200
+ derivas de algo que ya no es el aparato publicado. si no hay
201
+ manifiesto ---ejecución local desde el repo--- no hay nada que
202
+ contrastar y la función no hace nada.
203
+
204
+ Args:
205
+ raiz: directorio donde vive `manifiesto.sha256`.
206
+
207
+ Raises:
208
+ AssertionError: si algún fichero falta o su hash no cuadra.
209
+ """
210
+ import hashlib
211
+
212
+ m = pathlib.Path(raiz) / "manifiesto.sha256"
213
+ if not m.exists():
214
+ return
215
+ for linea in m.read_text(encoding="utf-8").splitlines():
216
+ if not linea.strip():
217
+ continue
218
+ esperado, rel = linea.split(" ", 1)
219
+ f = pathlib.Path(raiz) / rel
220
+ assert f.exists(), f"manifiesto: falta {rel}"
221
+ h = hashlib.sha256()
222
+ with open(f, "rb") as fh:
223
+ for b in iter(lambda: fh.read(1 << 20), b""):
224
+ h.update(b)
225
+ assert h.hexdigest() == esperado, (
226
+ f"manifiesto: {rel} ha derivado respecto al commit firmado; "
227
+ f"el Space no sirve un código que no es el del paper")
imgs/L0_fuerte_s1.png DELETED
Binary file (41.5 kB)
 
imgs/L0_fuerte_s2.png DELETED
Binary file (41.5 kB)
 
imgs/L0_media_s1.png DELETED
Binary file (41.4 kB)
 
imgs/L0_media_s2.png DELETED
Binary file (41.4 kB)
 
imgs/L0_suave_s1.png DELETED
Binary file (41.5 kB)
 
imgs/L0_suave_s2.png DELETED
Binary file (41.5 kB)
 
imgs/L10_fuerte_s1.png DELETED
Binary file (41.4 kB)
 
imgs/L10_fuerte_s2.png DELETED
Binary file (41.3 kB)
 
imgs/L10_media_s1.png DELETED
Binary file (41.3 kB)
 
imgs/L10_media_s2.png DELETED
Binary file (41.3 kB)
 
imgs/L10_suave_s1.png DELETED
Binary file (41.4 kB)
 
imgs/L10_suave_s2.png DELETED
Binary file (41.3 kB)
 
imgs/L11_fuerte_s1.png DELETED
Binary file (41.3 kB)
 
imgs/L11_fuerte_s2.png DELETED
Binary file (41.3 kB)
 
imgs/L11_media_s1.png DELETED
Binary file (41.3 kB)
 
imgs/L11_media_s2.png DELETED
Binary file (41.4 kB)
 
imgs/L11_suave_s1.png DELETED
Binary file (41.3 kB)
 
imgs/L11_suave_s2.png DELETED
Binary file (41.4 kB)
 
imgs/L1_fuerte_s1.png DELETED
Binary file (41.6 kB)
 
imgs/L1_fuerte_s2.png DELETED
Binary file (41.6 kB)
 
imgs/L1_media_s1.png DELETED
Binary file (41.6 kB)
 
imgs/L1_media_s2.png DELETED
Binary file (41.6 kB)
 
imgs/L1_suave_s1.png DELETED
Binary file (41.6 kB)
 
imgs/L1_suave_s2.png DELETED
Binary file (41.6 kB)
 
imgs/L2_fuerte_s1.png DELETED
Binary file (41.7 kB)
 
imgs/L2_fuerte_s2.png DELETED
Binary file (41.8 kB)
 
imgs/L2_media_s1.png DELETED
Binary file (41.8 kB)
 
imgs/L2_media_s2.png DELETED
Binary file (41.8 kB)
 
imgs/L2_suave_s1.png DELETED
Binary file (41.8 kB)
 
imgs/L2_suave_s2.png DELETED
Binary file (41.8 kB)
 
imgs/L3_fuerte_s1.png DELETED
Binary file (41.4 kB)
 
imgs/L3_fuerte_s2.png DELETED
Binary file (41.4 kB)
 
imgs/L3_media_s1.png DELETED
Binary file (41.4 kB)
 
imgs/L3_media_s2.png DELETED
Binary file (41.4 kB)
 
imgs/L3_suave_s1.png DELETED
Binary file (41.3 kB)
 
imgs/L3_suave_s2.png DELETED
Binary file (41.4 kB)
 
imgs/L4_fuerte_s1.png DELETED
Binary file (41.6 kB)
 
imgs/L4_fuerte_s2.png DELETED
Binary file (41.7 kB)
 
imgs/L4_media_s1.png DELETED
Binary file (41.6 kB)
 
imgs/L4_media_s2.png DELETED
Binary file (41.7 kB)
 
imgs/L4_suave_s1.png DELETED
Binary file (41.7 kB)
 
imgs/L4_suave_s2.png DELETED
Binary file (41.6 kB)
 
imgs/L5_fuerte_s1.png DELETED
Binary file (41.6 kB)