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.
@@ -0,0 +1,6 @@
1
+ """Dense-Armor - the wearable Sentinel shield: two-stage anomaly damping for any AI.
2
+ Author: Salvatore Pennacchio (Napoli, 2026). Sister project: Dense-Evolution."""
3
+ from .armatura import Armatura
4
+
5
+ __version__ = "1.0.0"
6
+ __all__ = ["Armatura"]
@@ -0,0 +1,3 @@
1
+ from .armatura import main
2
+
3
+ main()
@@ -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