dense-armor 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dense_armor/__init__.py +6 -0
- dense_armor/__main__.py +3 -0
- dense_armor/armatura.py +202 -0
- dense_armor/core/__init__.py +0 -0
- dense_armor/core/chunk.py +96 -0
- dense_armor/core/compiler.py +253 -0
- dense_armor/core/damping_operator.py +89 -0
- dense_armor/core/engine.py +426 -0
- dense_armor/core/init.py +35 -0
- dense_armor/core/logger.py +45 -0
- dense_armor/core/memory.py +114 -0
- dense_armor/core/noise.py +122 -0
- dense_armor/core/preset.py +80 -0
- dense_armor/core/profiler.py +88 -0
- dense_armor/core/tensor.py +69 -0
- dense_armor/core/vector.py +140 -0
- dense_armor/core/visualizer.py +103 -0
- dense_armor/utility/__init__.py +0 -0
- dense_armor/utility/anwav.py +54 -0
- dense_armor/utility/collatz.py +92 -0
- dense_armor/utility/curvature.py +21 -0
- dense_armor/utility/diagnostic.py +50 -0
- dense_armor/utility/iodat.py +32 -0
- dense_armor/utility/metro.py +31 -0
- dense_armor/utility/orca.py +227 -0
- dense_armor/utility/resonance_search.py +74 -0
- dense_armor-1.0.0.dist-info/METADATA +223 -0
- dense_armor-1.0.0.dist-info/RECORD +32 -0
- dense_armor-1.0.0.dist-info/WHEEL +5 -0
- dense_armor-1.0.0.dist-info/entry_points.txt +2 -0
- dense_armor-1.0.0.dist-info/licenses/LICENSE.md +42 -0
- dense_armor-1.0.0.dist-info/top_level.txt +1 -0
dense_armor/__init__.py
ADDED
dense_armor/__main__.py
ADDED
dense_armor/armatura.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
armatura.py — L'ARMATURA: istanza indossabile dello scudo Sentinel.
|
|
4
|
+
Versione standalone: usa i core/ e utility/ di QUESTA cartella (shield_),
|
|
5
|
+
nessuna dipendenza da BERT o da altri progetti.
|
|
6
|
+
|
|
7
|
+
Uso rapido (PowerShell, da questa cartella):
|
|
8
|
+
python armatura.py 1.2 1.3 9999 1.25 nan 1.3
|
|
9
|
+
python armatura.py file_con_numeri.txt
|
|
10
|
+
|
|
11
|
+
Uso da codice (qualsiasi IA):
|
|
12
|
+
from armatura import Armatura
|
|
13
|
+
a = Armatura(livello_ia=0.0) # 0=IA neonata (FILTRA) ... 1=matura (solo MARCA)
|
|
14
|
+
pulito, K, anomalie = a.analizza(serie)
|
|
15
|
+
pulito, K, anomalie = a.analizza(serie_oggi, riferimento=baseline_di_ieri) # anti-deriva
|
|
16
|
+
"""
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
import jax.numpy as jnp
|
|
23
|
+
|
|
24
|
+
from .core.engine import AdaptiveSignalStabilizer # scudo originale, intatto
|
|
25
|
+
from .utility.collatz import ABCollatz # scudo originale, intatto
|
|
26
|
+
from .utility.metro import Metro # scaler anti-underflow, intatto
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Armatura:
|
|
30
|
+
"""Scudo indossabile con CLIP DINAMICO universale.
|
|
31
|
+
|
|
32
|
+
livello_ia (0..1): quanto e' matura l'IA che lo indossa.
|
|
33
|
+
0.0 -> clip pieno: lo scudo FILTRA il dato (corazza per IA neonata)
|
|
34
|
+
1.0 -> clip zero: lo scudo NON tocca il dato, solo MARCA (lente per IA matura)
|
|
35
|
+
PAVIMENTO: la marcatura delle anomalie non viene MAI clippata — a nessun
|
|
36
|
+
livello lo scudo tace (l'anomalia puo' ESSERE la risposta: mai cancellarla
|
|
37
|
+
in silenzio). Il livello puo' essere misurato, non dichiarato:
|
|
38
|
+
Armatura.livello_da_entropia(entropia, vocab_size).
|
|
39
|
+
|
|
40
|
+
MEMORIA: lo scudo e' una funzione PURA, senza stato (leggero per design).
|
|
41
|
+
La memoria appartiene all'IA che lo indossa: per vedere la DERIVA LENTA
|
|
42
|
+
passa la tua baseline storica come `riferimento`.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, static_threshold=0.15, initial_damping=0.85, alpha=0.05,
|
|
46
|
+
soglia_anomalia=None, livello_ia=1.0):
|
|
47
|
+
self.stab = AdaptiveSignalStabilizer(static_threshold=static_threshold,
|
|
48
|
+
initial_damping=initial_damping, alpha=alpha)
|
|
49
|
+
self.shield = ABCollatz(epsilon_target=1.0)
|
|
50
|
+
self.soglia = soglia_anomalia
|
|
51
|
+
self.livello_ia = float(min(1.0, max(0.0, livello_ia)))
|
|
52
|
+
|
|
53
|
+
def set_livello(self, livello):
|
|
54
|
+
"""Aggiorna il livello dell'IA (0=debole -> filtra; 1=matura -> marca)."""
|
|
55
|
+
self.livello_ia = float(min(1.0, max(0.0, livello)))
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def livello_da_entropia(entropia, vocab_size):
|
|
59
|
+
"""Livello MISURATO: 1 - entropia/log(V) (stessa formula dello sfogo dinamico)."""
|
|
60
|
+
import math
|
|
61
|
+
return max(0.0, min(1.0, 1.0 - entropia / (math.log(max(2, vocab_size)) + 1e-12)))
|
|
62
|
+
|
|
63
|
+
def analizza(self, serie, riferimento=None):
|
|
64
|
+
"""serie: 1D (lista/array). Per tensori N-D: passare tensore.ravel()
|
|
65
|
+
e rifare reshape dopo (lo scudo e' agnostico: la trasposizione basta).
|
|
66
|
+
Ritorna (pulito, K, indici_anomalie)."""
|
|
67
|
+
s = np.asarray(serie, dtype=np.float64).reshape(1, -1)
|
|
68
|
+
s_jnp = jnp.array(s)
|
|
69
|
+
|
|
70
|
+
f1 = self.stab.filter_batch_scenarios(s_jnp) # Stadio 1
|
|
71
|
+
f1 = jnp.where(jnp.isnan(f1), jnp.nan_to_num(s_jnp), f1)
|
|
72
|
+
if riferimento is not None:
|
|
73
|
+
rif = jnp.array(np.asarray(riferimento, dtype=np.float64).reshape(1, -1))
|
|
74
|
+
else:
|
|
75
|
+
# riferimento cieco = smoothing pesante (pattern SentinelCV2D):
|
|
76
|
+
# mediana mobile immune agli spike, bersaglio per lo Stadio 2
|
|
77
|
+
v = np.array(f1).ravel()
|
|
78
|
+
n = v.size
|
|
79
|
+
rif_np = np.empty(n)
|
|
80
|
+
for _i in range(n):
|
|
81
|
+
a, b = max(0, _i - 3), min(n, _i + 4)
|
|
82
|
+
rif_np[_i] = np.median(v[a:b])
|
|
83
|
+
rif = jnp.array(rif_np.reshape(1, -1))
|
|
84
|
+
gt = self.shield.compute_damping_gating(f1, rif) # Stadio 2
|
|
85
|
+
gt = jnp.where(jnp.isnan(gt), 0.85, gt)
|
|
86
|
+
|
|
87
|
+
# PERCEZIONE a K pieno (pavimento: mai clippata)
|
|
88
|
+
K = np.array(gt).ravel()
|
|
89
|
+
pulito_pieno = np.array(f1 - gt * (f1 - rif)).ravel()
|
|
90
|
+
# INTERVENTO clippato dal livello dell'IA
|
|
91
|
+
clip = 1.0 - self.livello_ia
|
|
92
|
+
pulito = np.array(f1 - (gt * clip) * (f1 - rif)).ravel()
|
|
93
|
+
|
|
94
|
+
grezza = s.ravel()
|
|
95
|
+
dev = np.abs(np.nan_to_num(grezza) - pulito_pieno)
|
|
96
|
+
soglia = self.soglia if self.soglia is not None else (dev.mean() + 2.0 * dev.std())
|
|
97
|
+
anomalie = set(int(i) for i in np.where(dev > max(soglia, 1e-12))[0])
|
|
98
|
+
|
|
99
|
+
# rilevatore robusto (mediana/MAD) per gli spike che lo Stadio 1 insegue
|
|
100
|
+
finiti = grezza[np.isfinite(grezza)]
|
|
101
|
+
if finiti.size >= 4:
|
|
102
|
+
med = float(np.median(finiti))
|
|
103
|
+
mad = float(np.median(np.abs(finiti - med))) or 1e-12
|
|
104
|
+
z_rob = np.abs((np.nan_to_num(grezza, nan=med) - med) / (1.4826 * mad))
|
|
105
|
+
anomalie |= set(int(i) for i in np.where(z_rob > 6.0)[0])
|
|
106
|
+
|
|
107
|
+
anomalie |= {int(i) for i in np.where(~np.isfinite(grezza))[0]}
|
|
108
|
+
return pulito, K, sorted(anomalie)
|
|
109
|
+
|
|
110
|
+
def deriva(self, serie):
|
|
111
|
+
"""RILEVATORE DI DERIVA LENTA (usa METRO, come suggerito da Salvatore).
|
|
112
|
+
|
|
113
|
+
La rana bollita: incrementi infinitesimi (es. +0.004/passo) invisibili
|
|
114
|
+
allo scudo punto-per-punto. Qui si guarda la TENDENZA degli incrementi:
|
|
115
|
+
se la loro media e' sistematica rispetto al rumore, c'e' deriva — e
|
|
116
|
+
METRO (scaler logaritmico) ne risolve la scala anche quando e' sotto
|
|
117
|
+
la precisione visibile. Funziona SENZA baseline storica (ma la baseline
|
|
118
|
+
via `riferimento` in analizza() resta il metodo piu' forte).
|
|
119
|
+
|
|
120
|
+
Ritorna (tasso_per_passo, significativa: bool, esponente_metro).
|
|
121
|
+
"""
|
|
122
|
+
v = np.asarray(serie, dtype=np.float64).ravel()
|
|
123
|
+
v = v[np.isfinite(v)]
|
|
124
|
+
if v.size < 9:
|
|
125
|
+
return 0.0, False, 0.0
|
|
126
|
+
# la firma della deriva e' lo SPOSTAMENTO CUMULATIVO: mediana del primo
|
|
127
|
+
# terzo vs mediana dell'ultimo terzo (robusto agli spike), confrontato
|
|
128
|
+
# col rumore della serie. Gli incrementi singoli restano nel rumore —
|
|
129
|
+
# e' la somma che tradisce la rana.
|
|
130
|
+
terzo = v.size // 3
|
|
131
|
+
med_inizio = float(np.median(v[:terzo]))
|
|
132
|
+
med_fine = float(np.median(v[-terzo:]))
|
|
133
|
+
spostamento = med_fine - med_inizio
|
|
134
|
+
tasso = spostamento / max(1, v.size - terzo)
|
|
135
|
+
mad_serie = float(np.median(np.abs(v - np.median(v)))) or 1e-12
|
|
136
|
+
rumore_mediane = 1.4826 * mad_serie / np.sqrt(terzo)
|
|
137
|
+
significativa = abs(spostamento) > 3.0 * rumore_mediane
|
|
138
|
+
# METRO: risolve la scala dell'infinitesimo (leggibile anche a 1e-12)
|
|
139
|
+
_v_new, fact = Metro().enc(abs(tasso))
|
|
140
|
+
esponente = float(np.log10(fact)) - (-4.0) if fact > 0 else 0.0 # = -log10(|tasso|)
|
|
141
|
+
return tasso, bool(significativa), esponente
|
|
142
|
+
|
|
143
|
+
def referto_json(self, serie, nome="serie", riferimento=None):
|
|
144
|
+
"""Referto MACCHINA-LEGGIBILE (dict pronto per json.dumps).
|
|
145
|
+
Pensato per IA e altri programmi: niente prosa, solo dati.
|
|
146
|
+
I valori non finiti diventano stringhe ("NaN") per dare JSON valido."""
|
|
147
|
+
pulito, K, anomalie = self.analizza(serie, riferimento)
|
|
148
|
+
s = np.asarray(serie, dtype=np.float64).ravel()
|
|
149
|
+
tasso, sig, esp = self.deriva(serie)
|
|
150
|
+
def _num(v):
|
|
151
|
+
return float(v) if np.isfinite(v) else "NaN"
|
|
152
|
+
return {
|
|
153
|
+
"nome": nome,
|
|
154
|
+
"punti": int(s.size),
|
|
155
|
+
"livello_ia": self.livello_ia,
|
|
156
|
+
"anomalie": [{"indice": int(i), "grezzo": _num(s[i]),
|
|
157
|
+
"pulito": _num(pulito[i]), "K": _num(K[i])} for i in anomalie],
|
|
158
|
+
"K_medio": _num(np.mean(K)),
|
|
159
|
+
"deriva": {"tasso_per_passo": _num(tasso), "significativa": bool(sig),
|
|
160
|
+
"esponente_metro": _num(esp)},
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def referto(self, serie, nome="serie", riferimento=None):
|
|
164
|
+
"""Analisi + referto leggibile."""
|
|
165
|
+
pulito, K, anomalie = self.analizza(serie, riferimento)
|
|
166
|
+
s = np.asarray(serie, dtype=np.float64).ravel()
|
|
167
|
+
print(f"[ARMATURA] {nome}: {len(s)} punti | livello IA {self.livello_ia:.2f} | anomalie: {len(anomalie)}")
|
|
168
|
+
for i in anomalie[:10]:
|
|
169
|
+
print(f" -> indice {i}: valore {s[i]!r} (pulito: {pulito[i]:.4f}, K={K[i]:.3f})")
|
|
170
|
+
if len(anomalie) > 10:
|
|
171
|
+
print(f" ... e altre {len(anomalie) - 10}")
|
|
172
|
+
tasso, sig, esp = self.deriva(serie)
|
|
173
|
+
if sig:
|
|
174
|
+
print(f" ⚠ DERIVA LENTA rilevata (Metro): {tasso:+.2e} per passo (scala 10^-{esp:.1f})")
|
|
175
|
+
return anomalie
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def main():
|
|
179
|
+
import re as _re
|
|
180
|
+
argv = sys.argv[1:]
|
|
181
|
+
come_json = "--json" in argv
|
|
182
|
+
argv = [a for a in argv if a != "--json"]
|
|
183
|
+
if not argv:
|
|
184
|
+
print("USO: python armatura.py [--json] <file_con_numeri> oppure python armatura.py [--json] n1 n2 n3 ...")
|
|
185
|
+
sys.exit(0)
|
|
186
|
+
if len(argv) == 1 and os.path.exists(argv[0]):
|
|
187
|
+
testo = open(argv[0], encoding="utf-8", errors="ignore").read()
|
|
188
|
+
numeri = [float(x) if x.lower() != "nan" else float("nan")
|
|
189
|
+
for x in _re.findall(r"[-+]?(?:\d+\.?\d*(?:[eE][-+]?\d+)?|nan|NaN)", testo)]
|
|
190
|
+
nome = os.path.basename(argv[0])
|
|
191
|
+
else:
|
|
192
|
+
numeri = [float(x) if x.lower() != "nan" else float("nan") for x in argv]
|
|
193
|
+
nome = "serie da riga di comando"
|
|
194
|
+
if come_json:
|
|
195
|
+
import json as _json
|
|
196
|
+
print(_json.dumps(Armatura().referto_json(numeri, nome), ensure_ascii=False))
|
|
197
|
+
else:
|
|
198
|
+
Armatura().referto(numeri, nome)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""core/chunk.py.
|
|
3
|
+
|
|
4
|
+
===============================================================================
|
|
5
|
+
SENTINEL ENTERPRISE CHUNKER: SPAZIALE, QUANTISTICO & BEAST MODE OPTIMIZATION
|
|
6
|
+
===============================================================================
|
|
7
|
+
Modulo per la segmentazione di batch d'immagini e l'esecuzione cached XLA
|
|
8
|
+
delle ricette di compilazione senza overhead o ricompilazioni JIT.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import jax
|
|
12
|
+
import jax.numpy as jnp
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ImageChunker:
|
|
17
|
+
"""Gestore avanzato di chunking per tensori d'immagine e ricette JIT.
|
|
18
|
+
|
|
19
|
+
Risolve i colli di bottiglia della memoria XLA/JAX sia dividendo i grandi
|
|
20
|
+
batch di dati, sia spezzando le liste di istruzioni lunghe in blocchi a
|
|
21
|
+
dimensione fissa.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, chunk_size: int = 128) -> None:
|
|
25
|
+
self.chunk_size = int(chunk_size)
|
|
26
|
+
|
|
27
|
+
# ── SEZIONE SPAZIALE FOTO/BATCH (Per test21.py e main.py) ───────────────
|
|
28
|
+
|
|
29
|
+
def split_array(self, array: np.ndarray) -> list:
|
|
30
|
+
"""Spezza un array multidimensionale in una lista di sotto-chunk."""
|
|
31
|
+
total_samples = array.shape[0]
|
|
32
|
+
num_chunks = int(np.ceil(total_samples / self.chunk_size))
|
|
33
|
+
chunks = []
|
|
34
|
+
for b in range(num_chunks):
|
|
35
|
+
start_idx = b * self.chunk_size
|
|
36
|
+
end_idx = min(start_idx + self.chunk_size, total_samples)
|
|
37
|
+
chunks.append(array[start_idx:end_idx])
|
|
38
|
+
return chunks
|
|
39
|
+
|
|
40
|
+
def merge_chunks(self, chunks_list: list) -> np.ndarray:
|
|
41
|
+
"""Ricombina una lista di sotto-chunk in un unico array compatto."""
|
|
42
|
+
if not chunks_list:
|
|
43
|
+
return np.array([], dtype=np.float32)
|
|
44
|
+
# Se l'input è monodimensionale flat, usa concatenate invece di vstack
|
|
45
|
+
if chunks_list[0].ndim == 1:
|
|
46
|
+
return np.concatenate(chunks_list)
|
|
47
|
+
return np.vstack(chunks_list)
|
|
48
|
+
|
|
49
|
+
# ── SEZIONE BEAST MODE COMPILER (Estratta dalla logica quantistica) ─────
|
|
50
|
+
|
|
51
|
+
def execute_pipeline_beast_mode(
|
|
52
|
+
self, codegen_engine, input_vector: np.ndarray, compiled_ops: list
|
|
53
|
+
) -> np.ndarray:
|
|
54
|
+
"""Esegue le istruzioni del compilatore a blocchi fissi (chunk_size).
|
|
55
|
+
|
|
56
|
+
Trapianto logico di 'run_circuit_with_chunking'. Impedisce a XLA di
|
|
57
|
+
ricompilare la ricetta IA se cambia il numero di istruzioni.
|
|
58
|
+
"""
|
|
59
|
+
output = jnp.array(input_vector, dtype=jnp.float64)
|
|
60
|
+
|
|
61
|
+
# Spezza ed esegue la lista di comandi/operazioni
|
|
62
|
+
for i in range(0, len(compiled_ops), self.chunk_size):
|
|
63
|
+
chunk_ops = compiled_ops[i : i + self.chunk_size]
|
|
64
|
+
output = codegen_engine.run_pipeline_with_chunking(
|
|
65
|
+
output, chunk_ops, chunk_size=self.chunk_size
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return np.array(output, dtype=np.float64)
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def patch_and_scan_parameters(
|
|
72
|
+
template_ops: jnp.ndarray, dynamic_parameters: jnp.ndarray
|
|
73
|
+
) -> jnp.ndarray:
|
|
74
|
+
"""Iniezione dinamica di parametri e pesi in un ciclo nativo JAX JIT.
|
|
75
|
+
|
|
76
|
+
Trapianto logico di 'patch_and_apply' tramite jax.lax.scan. Rileva i
|
|
77
|
+
punti di iniezione contrassegnati dal marcatore -1.0.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def patch_single_op(carry, op):
|
|
81
|
+
idx = carry
|
|
82
|
+
# Se rileva lo slot parametrico quantistico/lineare attivo (-1.0)
|
|
83
|
+
is_parametric = op == -1.0
|
|
84
|
+
final_param = jnp.where(is_parametric, dynamic_parameters[idx], op)
|
|
85
|
+
next_idx = jnp.where(is_parametric, idx + jnp.int32(1), idx)
|
|
86
|
+
|
|
87
|
+
# Restituisce l'operazione patchata a basso livello XLA
|
|
88
|
+
patched_op = jnp.array(
|
|
89
|
+
[op, op, op, final_param], dtype=jnp.float64
|
|
90
|
+
)
|
|
91
|
+
return next_idx, patched_op
|
|
92
|
+
|
|
93
|
+
_, patched_compiled_ops = jax.lax.scan(
|
|
94
|
+
patch_single_op, jnp.int32(0), template_ops
|
|
95
|
+
)
|
|
96
|
+
return patched_compiled_ops
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
core/compiler.py
|
|
4
|
+
==============================
|
|
5
|
+
DynamicAICodegen — compila ricette testuali in pipeline JAX eseguibili.
|
|
6
|
+
|
|
7
|
+
Fix applicati
|
|
8
|
+
-------------
|
|
9
|
+
- BILANCIAMENTO AUREO: Integrazione dei poli di accoppiamento basati sulla costante PHI.
|
|
10
|
+
- INTEGRITÀ XLA: Tipi forzati lato CPU per prevenire eccezioni sui file binari (.bin).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import jax
|
|
15
|
+
import jax.numpy as jnp
|
|
16
|
+
import numpy as np
|
|
17
|
+
|
|
18
|
+
# =========================================================================
|
|
19
|
+
# ANCORAGGIO GEOMETRICO E BAKING DELLO SPETTRO (XLA BINARY SAFE)
|
|
20
|
+
# =========================================================================
|
|
21
|
+
_PHI_128 = np.longdouble(1.0 + np.sqrt(5.0)) / 2.0
|
|
22
|
+
_PHI = float(_PHI_128)
|
|
23
|
+
_ALPHA = float(0.25)
|
|
24
|
+
_PHI_FOUR = float(_PHI_128 ** 4) # Moltiplicatore topologico della barriera (~6.854)
|
|
25
|
+
|
|
26
|
+
# ── Mappa comandi → branch index (contigui 0-N per lax.switch) ───────────────
|
|
27
|
+
CMD_MAP = {
|
|
28
|
+
'identity': 0,
|
|
29
|
+
'relu': 1,
|
|
30
|
+
'sigmoid': 2,
|
|
31
|
+
'tanh': 3,
|
|
32
|
+
'scale': 4,
|
|
33
|
+
'dropout': 5,
|
|
34
|
+
'clip': 6,
|
|
35
|
+
'l2_normalize':7,
|
|
36
|
+
}
|
|
37
|
+
_N_BRANCHES = 8
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ── step JIT ─────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
@jax.jit
|
|
43
|
+
def _execute_ai_instruction_step(carry, instruction: jnp.ndarray):
|
|
44
|
+
"""
|
|
45
|
+
Esegue un singolo passo della pipeline AI su JAX.
|
|
46
|
+
carry = (data_vector, prng_key)
|
|
47
|
+
instruction = [cmd_id, p1, p2, _reserved]
|
|
48
|
+
"""
|
|
49
|
+
data, prng_key = carry
|
|
50
|
+
cmd_id = instruction[0].astype(jnp.int32)
|
|
51
|
+
p1, p2 = instruction[1], instruction[2]
|
|
52
|
+
|
|
53
|
+
# ── Branch helpers ────────────────────────────────────────────────────────
|
|
54
|
+
def _identity(args):
|
|
55
|
+
d, _p1, _p2, _k = args
|
|
56
|
+
return d, _k
|
|
57
|
+
|
|
58
|
+
def _relu(args):
|
|
59
|
+
d, _p1, _p2, _k = args
|
|
60
|
+
return jnp.maximum(0.0, d), _k
|
|
61
|
+
|
|
62
|
+
def _sigmoid(args):
|
|
63
|
+
d, _p1, _p2, _k = args
|
|
64
|
+
return jax.nn.sigmoid(d), _k
|
|
65
|
+
|
|
66
|
+
def _tanh(args):
|
|
67
|
+
d, _p1, _p2, _k = args
|
|
68
|
+
return jnp.tanh(d), _k
|
|
69
|
+
|
|
70
|
+
def _scale(args):
|
|
71
|
+
d, _p1, _p2, _k = args
|
|
72
|
+
return d * _p1 + _p2, _k
|
|
73
|
+
|
|
74
|
+
def _dropout(args):
|
|
75
|
+
d, _p1, _p2, _k = args
|
|
76
|
+
next_k, subkey = jax.random.split(_k)
|
|
77
|
+
safe_p1 = jnp.where(_p1 > 0.0, _p1, 1.0)
|
|
78
|
+
mask = jax.random.bernoulli(subkey, p=safe_p1, shape=d.shape)
|
|
79
|
+
return jnp.where(mask, d / safe_p1, 0.0), next_k
|
|
80
|
+
|
|
81
|
+
def _clip(args):
|
|
82
|
+
d, _p1, _p2, _k = args
|
|
83
|
+
return jnp.clip(d, _p1, _p2), _k
|
|
84
|
+
|
|
85
|
+
def _l2_normalize(args):
|
|
86
|
+
d, _p1, _p2, _k = args
|
|
87
|
+
norm = jnp.linalg.norm(d)
|
|
88
|
+
safe_n = jnp.where(norm > 0.0, norm, 1.0)
|
|
89
|
+
return d / safe_n, _k
|
|
90
|
+
|
|
91
|
+
branches = [_identity, _relu, _sigmoid, _tanh, _scale, _dropout, _clip, _l2_normalize]
|
|
92
|
+
|
|
93
|
+
safe_cmd = jnp.clip(cmd_id, 0, _N_BRANCHES - 1)
|
|
94
|
+
result, next_key = jax.lax.switch(safe_cmd, branches, (data, p1, p2, prng_key))
|
|
95
|
+
|
|
96
|
+
return (result, next_key), None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
100
|
+
# DynamicAICodegen (Inizio Classe)
|
|
101
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
class DynamicAICodegen:
|
|
104
|
+
"""
|
|
105
|
+
Compila ricette testuali in matrici di istruzioni JAX ed esegue pipeline
|
|
106
|
+
con chunking Anti-OOM e calcolo del gradiente via JAX AD.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def __init__(self, seed: int = 42):
|
|
110
|
+
self.cmd_map = CMD_MAP
|
|
111
|
+
self.base_key = jax.random.PRNGKey(seed)
|
|
112
|
+
|
|
113
|
+
# ── Compilazione ──────────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
def compile_pipeline(self, text_instructions: list) -> np.ndarray:
|
|
116
|
+
"""
|
|
117
|
+
Converte una lista di istruzioni testuali/tuple in matrice float64
|
|
118
|
+
di shape (N, 4): [cmd_id, p1, p2, reserved].
|
|
119
|
+
"""
|
|
120
|
+
compiled = []
|
|
121
|
+
for cmd in text_instructions:
|
|
122
|
+
if isinstance(cmd, tuple):
|
|
123
|
+
name = str(cmd[0]).lower().strip()
|
|
124
|
+
p1 = float(cmd[1]) if len(cmd) > 1 else 0.0
|
|
125
|
+
p2 = float(cmd[2]) if len(cmd) > 2 else 0.0
|
|
126
|
+
else:
|
|
127
|
+
name = str(cmd).lower().strip()
|
|
128
|
+
if name == 'dropout':
|
|
129
|
+
p1, p2 = 0.8, 0.0
|
|
130
|
+
elif name == 'clip':
|
|
131
|
+
p1, p2 = -1.0, 1.0
|
|
132
|
+
elif name == 'scale':
|
|
133
|
+
p1, p2 = float(_PHI), float(_ALPHA) # <-- Sintonizzazione d'Asse Geometrica
|
|
134
|
+
else:
|
|
135
|
+
p1, p2 = 0.0, 0.0
|
|
136
|
+
|
|
137
|
+
cmd_id = self.cmd_map.get(name, 0)
|
|
138
|
+
compiled.append([float(cmd_id), p1, p2, 0.0])
|
|
139
|
+
|
|
140
|
+
return np.array(compiled, dtype=np.float64)
|
|
141
|
+
|
|
142
|
+
# ── Esecuzione dinamica ───────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
def run_dynamic_pipeline(
|
|
145
|
+
self,
|
|
146
|
+
input_data: np.ndarray,
|
|
147
|
+
compiled_ops: np.ndarray,
|
|
148
|
+
) -> np.ndarray:
|
|
149
|
+
"""Esegue la pipeline compilata in un singolo lax.scan JIT."""
|
|
150
|
+
|
|
151
|
+
@jax.jit
|
|
152
|
+
def _run(d, ops, k):
|
|
153
|
+
(f_data, next_k), _ = jax.lax.scan(
|
|
154
|
+
_execute_ai_instruction_step, (d, k), ops
|
|
155
|
+
)
|
|
156
|
+
return f_data, next_k
|
|
157
|
+
|
|
158
|
+
res_data, updated_key = _run(
|
|
159
|
+
jnp.array(input_data, dtype=jnp.float64),
|
|
160
|
+
jnp.array(compiled_ops, dtype=jnp.float64),
|
|
161
|
+
self.base_key,
|
|
162
|
+
)
|
|
163
|
+
self.base_key = updated_key
|
|
164
|
+
return np.array(res_data)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# ── Chunked execution (Anti-OOM) Advanced via Grafo Nativo XLA ───────────
|
|
168
|
+
|
|
169
|
+
def run_pipeline_with_chunking(
|
|
170
|
+
self,
|
|
171
|
+
input_data: np.ndarray,
|
|
172
|
+
compiled_ops: np.ndarray,
|
|
173
|
+
chunk_size: int = 500,
|
|
174
|
+
) -> np.ndarray:
|
|
175
|
+
"""
|
|
176
|
+
[ADVANCED ENGINE]: Suddivide la pipeline in blocchi ed esegue il chunking
|
|
177
|
+
interamente all'interno dell'acceleratore hardware senza colli di bottiglia CPU.
|
|
178
|
+
"""
|
|
179
|
+
n_ops = len(compiled_ops)
|
|
180
|
+
# Calcolo dei blocchi necessari preservando la conformazione statica
|
|
181
|
+
n_chunks = (n_ops + chunk_size - 1) // chunk_size
|
|
182
|
+
total_slots = n_chunks * chunk_size
|
|
183
|
+
|
|
184
|
+
# Allocazione della matrice di padding condizionata
|
|
185
|
+
padded_ops = np.zeros((total_slots, 4), dtype=np.float64)
|
|
186
|
+
padded_ops[:n_ops] = compiled_ops
|
|
187
|
+
|
|
188
|
+
# Riorganizzazione geometrica in tensore 3D (N_Chunks x Chunk_Size x 4)
|
|
189
|
+
structured_chunks = padded_ops.reshape(n_chunks, chunk_size, 4)
|
|
190
|
+
|
|
191
|
+
j_data = jnp.array(input_data, dtype=jnp.float64)
|
|
192
|
+
j_chunks = jnp.array(structured_chunks, dtype=jnp.float64)
|
|
193
|
+
|
|
194
|
+
@jax.jit
|
|
195
|
+
def _chunked_scan_compiler(data_state, chunks_tensor):
|
|
196
|
+
# Scan a due livelli: itera sui macro-chunk mantenendo intatto lo stato d'onda JAX
|
|
197
|
+
def _chunk_iterator(carry_state, single_chunk):
|
|
198
|
+
(f_data, next_k), _ = jax.lax.scan(
|
|
199
|
+
_execute_ai_instruction_step, (carry_state, self.base_key), single_chunk
|
|
200
|
+
)
|
|
201
|
+
return f_data, None
|
|
202
|
+
|
|
203
|
+
final_state, _ = jax.lax.scan(_chunk_iterator, data_state, chunks_tensor)
|
|
204
|
+
return final_state
|
|
205
|
+
|
|
206
|
+
res_data = _chunked_scan_compiler(j_data, j_chunks)
|
|
207
|
+
return np.array(res_data)
|
|
208
|
+
|
|
209
|
+
# ── Gradients con Regolarizzazione Topologica Aurea ───────────────────────
|
|
210
|
+
|
|
211
|
+
def compute_gradients(
|
|
212
|
+
self,
|
|
213
|
+
input_data: np.ndarray,
|
|
214
|
+
compiled_ops: np.ndarray,
|
|
215
|
+
) -> np.ndarray:
|
|
216
|
+
"""Calcola i gradienti AD della loss, normalizzata da una costante fissa (_PHI_FOUR)."""
|
|
217
|
+
|
|
218
|
+
def _topological_loss_fn(d, ops, k):
|
|
219
|
+
(f_data, _), _ = jax.lax.scan(
|
|
220
|
+
_execute_ai_instruction_step, (d, k), ops
|
|
221
|
+
)
|
|
222
|
+
# Normalizzazione: l'errore quadratico e' scalato da una costante fissa
|
|
223
|
+
return jnp.sum(jnp.square(f_data)) / jnp.float64(_PHI_FOUR)
|
|
224
|
+
|
|
225
|
+
grad_engine = jax.jit(jax.grad(_topological_loss_fn, argnums=0))
|
|
226
|
+
j_input = jnp.array(input_data, dtype=jnp.float64)
|
|
227
|
+
j_ops = jnp.array(compiled_ops, dtype=jnp.float64)
|
|
228
|
+
|
|
229
|
+
grads = grad_engine(j_input, j_ops, self.base_key)
|
|
230
|
+
self.base_key = jax.random.split(self.base_key)[0]
|
|
231
|
+
return np.array(grads)
|
|
232
|
+
|
|
233
|
+
# ── Persistence ───────────────────────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
def save_compiled_pipeline(
|
|
236
|
+
self,
|
|
237
|
+
compiled_ops: np.ndarray,
|
|
238
|
+
filename: str = "compiled_recipe.npy",
|
|
239
|
+
):
|
|
240
|
+
"""Salva la matrice delle operazioni compilate in formato binario compresso .npy."""
|
|
241
|
+
np.save(filename, compiled_ops)
|
|
242
|
+
print(f"-> Advanced Pipeline salvata con successo: '{filename}'")
|
|
243
|
+
|
|
244
|
+
def load_compiled_pipeline(
|
|
245
|
+
self,
|
|
246
|
+
filename: str = "compiled_recipe.npy",
|
|
247
|
+
) -> np.ndarray:
|
|
248
|
+
"""Carica una matrice di operazioni precedentemente salvata."""
|
|
249
|
+
if not os.path.exists(filename):
|
|
250
|
+
raise FileNotFoundError(f"File pipeline assente: '{filename}'")
|
|
251
|
+
ops = np.load(filename)
|
|
252
|
+
print(f"-> Advanced Pipeline caricata con successo: '{filename}' shape={ops.shape}")
|
|
253
|
+
return ops
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
r"""
|
|
3
|
+
Sentinel Metrology Framework - Adaptive Damping Blend Operator
|
|
4
|
+
=========================================================
|
|
5
|
+
Sottosistema: fonde due segnali (es. output grezzo e riferimento) con un
|
|
6
|
+
guadagno non lineare che dipende dalla loro distanza -- differenza piccola
|
|
7
|
+
tra i due -> si preferisce il segnale grezzo; differenza grande -> si
|
|
8
|
+
preferisce il riferimento, in modo smorzato e sempre limitato.
|
|
9
|
+
Autore del Framework: Salvatore Pennacchio (Napoli, 2026)
|
|
10
|
+
Percorso: sentinel02/core/damping_operator.py
|
|
11
|
+
|
|
12
|
+
Nota: non e' calcolo quantistico. I nomi delle costanti (PHI, ecc.) sono
|
|
13
|
+
iperparametri numerici fissi, non stati/operatori quantistici -- non c'e'
|
|
14
|
+
aritmetica complessa ne' spazio di Hilbert coinvolti.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import jax
|
|
18
|
+
import jax.numpy as jnp
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
# =========================================================================
|
|
22
|
+
# COSTANTI NUMERICHE FISSE (XLA-SAFE, precompilate per stabilita' binaria)
|
|
23
|
+
# =========================================================================
|
|
24
|
+
_PHI_128 = np.longdouble(1.0 + np.sqrt(5.0)) / 2.0 # sezione aurea, usata solo come iperparametro
|
|
25
|
+
_ZETA_TOTAL_128 = np.longdouble(60.16762236065194) # costante numerica fissa (limite superiore di damping)
|
|
26
|
+
|
|
27
|
+
# Cast a tipi primitivi per prevenire eccezioni hardware nei file binari (.bin)
|
|
28
|
+
_PHI = float(_PHI_128)
|
|
29
|
+
_ALPHA = float(0.25) # Base simmetrica (1/4)
|
|
30
|
+
_SIGMA = float(np.longdouble(1.0) / (_PHI_128 ** 4)) # ~0.1458980337
|
|
31
|
+
_K_MAX_ZETA = float(_ZETA_TOTAL_128 / (_PHI_128 ** 4)) # Limite di damping superiore (~8.778)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@jax.jit
|
|
35
|
+
def apply_damping_blend(gradient_state: jnp.ndarray, noise_matrix: jnp.ndarray) -> jnp.ndarray:
|
|
36
|
+
r"""
|
|
37
|
+
Fonde `gradient_state` (segnale grezzo) e `noise_matrix` (riferimento)
|
|
38
|
+
con un coefficiente K variabile:
|
|
39
|
+
- differenza piccola tra i due segnali -> K basso, si preferisce il grezzo.
|
|
40
|
+
- differenza grande -> K alto, si preferisce il riferimento (smorzamento).
|
|
41
|
+
- [PATCH 2026]: isola e cancella i NaN a runtime prima del blend.
|
|
42
|
+
"""
|
|
43
|
+
eps = 1e-7
|
|
44
|
+
|
|
45
|
+
# =========================================================================
|
|
46
|
+
# SANIFICAZIONE NaN (nessun valore non finito deve propagarsi nel blend)
|
|
47
|
+
# =========================================================================
|
|
48
|
+
is_nan_gradient = jnp.isnan(gradient_state)
|
|
49
|
+
is_nan_noise = jnp.isnan(noise_matrix)
|
|
50
|
+
|
|
51
|
+
gradient_state_safe = jnp.where(is_nan_gradient, noise_matrix, gradient_state)
|
|
52
|
+
noise_matrix_safe = jnp.where(is_nan_noise, gradient_state_safe, noise_matrix)
|
|
53
|
+
# =========================================================================
|
|
54
|
+
|
|
55
|
+
# 1. Distanza assoluta tra i due segnali
|
|
56
|
+
diff = jnp.abs(gradient_state_safe - noise_matrix_safe)
|
|
57
|
+
|
|
58
|
+
# 2. Coerenza relativa: quanto la distanza e' piccola rispetto alla scala del segnale
|
|
59
|
+
c_gate = jnp.float32(_ALPHA + _SIGMA)
|
|
60
|
+
coherence = jnp.clip(1.0 - (diff / (2.0 * jnp.maximum(jnp.abs(gradient_state_safe), 1e-3))), 0.0, 1.0)
|
|
61
|
+
|
|
62
|
+
# 3. Curva di damping a sigmoide: sale gradualmente con la distanza, satura a _K_MAX_ZETA
|
|
63
|
+
x_steer = jnp.clip(diff, 0.0, 5.0)
|
|
64
|
+
steering = 1.0 / (1.0 + jnp.exp(-(x_steer - 2.0)))
|
|
65
|
+
h_damping = steering * jnp.float32(_K_MAX_ZETA)
|
|
66
|
+
|
|
67
|
+
# 4. Guadagno anomalo: decresce iperbolicamente con la distanza, poi limitato
|
|
68
|
+
c_anom = jnp.float32(1.0 / (_PHI ** 2))
|
|
69
|
+
K_anomalous_raw = c_anom / (c_anom + diff + eps)
|
|
70
|
+
K_anomalous = jnp.clip(K_anomalous_raw, jnp.float32(_ALPHA), jnp.float32(_K_MAX_ZETA))
|
|
71
|
+
|
|
72
|
+
# 5. Combinazione dei due guadagni pesata dalla coerenza
|
|
73
|
+
K_operator = (1.0 - coherence) * h_damping + coherence * K_anomalous
|
|
74
|
+
|
|
75
|
+
# Se uno degli input originari era NaN, forziamo K al massimo smorzamento
|
|
76
|
+
K_operator = jnp.where(jnp.logical_or(is_nan_gradient, is_nan_noise), jnp.float32(1.0 - (_ALPHA - _SIGMA)), K_operator)
|
|
77
|
+
|
|
78
|
+
# Limitazione dinamica del guadagno per evitare blend degeneri (K=0 o K=1 assoluti)
|
|
79
|
+
k_min_bound = jnp.float32(_ALPHA - _SIGMA)
|
|
80
|
+
k_max_bound = jnp.float32(1.0 - (_ALPHA - _SIGMA))
|
|
81
|
+
K_operator = jnp.clip(K_operator, k_min_bound, k_max_bound)
|
|
82
|
+
|
|
83
|
+
# Blend finale: media pesata tra segnale grezzo e riferimento
|
|
84
|
+
blended = (1.0 - K_operator) * gradient_state_safe + K_operator * noise_matrix_safe
|
|
85
|
+
|
|
86
|
+
# Pulizia finale: nessun NaN deve uscire dalla funzione
|
|
87
|
+
blended = jnp.where(jnp.isnan(blended), noise_matrix_safe, blended)
|
|
88
|
+
|
|
89
|
+
return blended
|