#include "memory.h" #include #include #include #include #ifdef _WIN32 #include #define make_dir(p) _mkdir(p) #else #include #define make_dir(p) mkdir(p, 0755) #endif static uint32_t crc32(const uint8_t *p, size_t n) { uint32_t c = 0xFFFFFFFFu; for (size_t i = 0; i < n; i++) { c ^= p[i]; for (int k = 0; k < 8; k++) c = (c >> 1) ^ (0xEDB88320u & (0u - (c & 1))); } return ~c; } static void layer_file(const MemoryStore *m, int layer, char *out, size_t cap) { snprintf(out, cap, "%s/strato_%02d.bin", m->dir, layer); } // ---- mappa (navigatore) --------------------------------------------------------------------- static void save_map(const MemoryStore *m) { char path[1100], tmp[1110]; snprintf(path, sizeof path, "%s/mappa.txt", m->dir); snprintf(tmp, sizeof tmp, "%s.tmp", path); FILE *f = fopen(tmp, "wb"); if (!f) return; fprintf(f, "# Mappa della memoria di Neuronal: id\tbyte\tcrc\tdata\ttitolo\tgruppi\timpronta\n"); for (int i = 0; i < m->n_mems; i++) { const Memory *x = &m->mems[i]; fprintf(f, "%d\t%d\t%08x\t%lld\t%s\t", x->id, x->len, x->crc, (long long)x->t, x->title); for (int k = 0; k < x->n_groups; k++) fprintf(f, "%s%d", k ? "," : "", x->groups[k]); fputc('\t', f); for (int k = 0; k < x->n_key; k++) fprintf(f, "%s%d:%.3f", k ? "," : "", x->key_g[k], x->key_w[k]); fputc('\n', f); } fclose(f); remove(path); rename(tmp, path); } static void emb_path(const MemoryStore *m, int id, char *out, size_t cap) { snprintf(out, cap, "%s/impronte/%d.bin", m->dir, id); } static void save_emb(const MemoryStore *m, const Memory *x) { char dir[1100], path[1200]; snprintf(dir, sizeof dir, "%s/impronte", m->dir); make_dir(dir); emb_path(m, x->id, path, sizeof path); FILE *f = fopen(path, "wb"); if (f) { fwrite(x->emb, sizeof(float), (size_t)m->emb_dim, f); fclose(f); } } static float *load_emb(const MemoryStore *m, int id) { char path[1200]; emb_path(m, id, path, sizeof path); FILE *f = fopen(path, "rb"); if (!f) return NULL; float *e = malloc(sizeof(float) * (size_t)m->emb_dim); bool ok = fread(e, sizeof(float), (size_t)m->emb_dim, f) == (size_t)m->emb_dim; fclose(f); if (!ok) { free(e); return NULL; } return e; } static void load_map(MemoryStore *m) { char path[1100]; snprintf(path, sizeof path, "%s/mappa.txt", m->dir); FILE *f = fopen(path, "rb"); if (!f) return; static char line[1 << 17]; while (fgets(line, sizeof line, f)) { if (line[0] == '#') continue; line[strcspn(line, "\r\n")] = 0; char *fld[7] = {0}; int n = 0; for (char *p = line; n < 7;) { fld[n++] = p; char *t = strchr(p, '\t'); if (!t) break; *t = 0; p = t + 1; } if (n < 7) continue; if (m->n_mems == m->cap_mems) { m->cap_mems = m->cap_mems ? m->cap_mems * 2 : 16; m->mems = realloc(m->mems, sizeof(Memory) * (size_t)m->cap_mems); } Memory *x = &m->mems[m->n_mems]; memset(x, 0, sizeof *x); x->id = atoi(fld[0]); x->len = atoi(fld[1]); x->crc = (uint32_t)strtoul(fld[2], NULL, 16); x->t = (time_t)atoll(fld[3]); snprintf(x->title, sizeof x->title, "%s", fld[4]); int cap = 16; x->groups = malloc(sizeof(int) * (size_t)cap); for (char *p = fld[5]; *p;) { if (x->n_groups == cap) { cap *= 2; x->groups = realloc(x->groups, sizeof(int) * (size_t)cap); } x->groups[x->n_groups++] = (int)strtol(p, &p, 10); if (*p == ',') p++; } for (char *p = fld[6]; *p && x->n_key < MEM_KEY;) { x->key_g[x->n_key] = (int)strtol(p, &p, 10); if (*p == ':') p++; x->key_w[x->n_key++] = strtof(p, &p); if (*p == ',') p++; } bool ok = true; for (int k = 0; k < x->n_groups; k++) ok &= x->groups[k] >= 0 && x->groups[k] < m->n_groups; if (!ok) { free(x->groups); continue; } for (int k = 0; k < x->n_groups; k++) m->owner[x->groups[k]] = x->id; if (x->id >= m->next_id) m->next_id = x->id + 1; x->emb = load_emb(m, x->id); m->n_mems++; } fclose(f); } bool mem_open(MemoryStore *m, const char *dir, int n_layer, int n_ff, int n_embd) { memset(m, 0, sizeof *m); snprintf(m->dir, sizeof m->dir, "%s", dir); m->n_layer = n_layer; m->n_ff = n_ff; m->per_layer = (n_ff + MEM_GROUP - 1) / MEM_GROUP; m->n_groups = m->per_layer * n_layer; m->owner = calloc((size_t)m->n_groups, sizeof(int)); m->next_id = 1; m->n_embd = n_embd; m->emb_dim = MEM_EMB_LAYERS * n_embd; static const float at[MEM_EMB_LAYERS] = {0.625f}; // strato 40 di 64: il migliore nelle prove del navigatore for (int k = 0; k < MEM_EMB_LAYERS; k++) m->emb_layer[k] = (int)(at[k] * n_layer); make_dir(dir); // un file per strato, 16 byte per gruppo: creato vuoto la prima volta size_t bytes = (size_t)m->per_layer * MEM_SLOT; uint8_t *zero = calloc(bytes, 1); for (int l = 0; l < n_layer; l++) { char path[1100]; layer_file(m, l, path, sizeof path); FILE *f = fopen(path, "rb"); long size = -1; if (f) { fseek(f, 0, SEEK_END); size = ftell(f); fclose(f); } if (size != (long)bytes) { f = fopen(path, size < 0 ? "wb" : "r+b"); if (!f) f = fopen(path, "wb"); if (!f) { free(zero); return false; } if (size < 0 || size < (long)bytes) { fseek(f, size < 0 ? 0 : size, SEEK_SET); fwrite(zero, 1, bytes - (size_t)(size < 0 ? 0 : size), f); } fclose(f); } } free(zero); load_map(m); return true; } void mem_set_baseline(MemoryStore *m, const Atlas *a) { free(m->mu); free(m->bg_mu); free(m->bg_sd); m->bg_mu = m->bg_sd = NULL; if (a->group_act && a->n_groups == m->n_groups && a->n_act > 1) { // comportamento normale di ogni gruppo m->bg_mu = calloc((size_t)m->n_groups, sizeof(float)); m->bg_sd = calloc((size_t)m->n_groups, sizeof(float)); for (int g = 0; g < m->n_groups; g++) { double s = 0, s2 = 0; for (int p = 0; p < a->n_act; p++) { double v = a->group_act[(size_t)p * a->n_groups + g]; s += v; s2 += v * v; } double mu = s / a->n_act, var = s2 / a->n_act - mu * mu; m->bg_mu[g] = (float)mu; m->bg_sd[g] = (float)fmax(sqrt(var > 0 ? var : 0), 0.05 * mu + 1e-6); } } m->mu = calloc((size_t)m->n_layer, sizeof(float)); for (int l = 0; l < m->n_layer && l < a->n_layer; l++) { double s = 0; for (int p = 0; p < a->n_act; p++) s += a->layer_act[(size_t)p * a->n_layer + l]; m->mu[l] = a->n_act ? (float)(s / a->n_act) : 0; } } void mem_close(MemoryStore *m) { for (int i = 0; i < m->n_mems; i++) { free(m->mems[i].groups); free(m->mems[i].emb); } free(m->mems); free(m->owner); free(m->mu); free(m->bg_mu); free(m->bg_sd); free(m->emb_mu); free(m->emb_sd); memset(m, 0, sizeof *m); } Memory *mem_find(MemoryStore *m, int id) { for (int i = 0; i < m->n_mems; i++) if (m->mems[i].id == id) return &m->mems[i]; return NULL; } long mem_used_groups(const MemoryStore *m) { long n = 0; for (int g = 0; g < m->n_groups; g++) n += m->owner[g] != 0; return n; } // ---- attivazione dei gruppi ------------------------------------------------------------------ // Il cervello legge il testo dentro una formula che gli fa condensare il significato sull'ultima parola: // l'attività dei gruppi in quel punto è l'impronta del testo. static void normalize_emb(const MemoryStore *m, float *e) { double ss = 0; for (int i = 0; i < m->emb_dim; i++) { if (m->emb_mu) e[i] -= m->emb_mu[i]; ss += (double)e[i] * e[i]; } float inv = ss > 0 ? (float)(1.0 / sqrt(ss)) : 0; for (int i = 0; i < m->emb_dim; i++) e[i] *= inv; } static bool read_text(MemoryStore *m, Brain *b, const char *text, float *vec, float *emb_raw) { BrainInfo info = brain_info(b); size_t tl = strlen(text); if (tl > 1500) { // un messaggio lungo: basta l'inizio per capire di cosa parla tl = 1500; while (tl > 0 && ((unsigned char)text[tl] & 0xC0) == 0x80) tl--; } char *prompt = malloc(tl + 96); sprintf(prompt, "Frase: \"%.*s\"\nIn una parola, questa frase parla di:", (int)tl, text); if (emb_raw) brain_capture_begin(b); int n = brain_feed(b, prompt); if (emb_raw) { // stato interno medio su tutte le parole lette long nt; const double *sum = brain_capture_x(b, &nt); for (int k = 0; k < MEM_EMB_LAYERS; k++) for (int d = 0; d < m->n_embd; d++) emb_raw[(size_t)k * m->n_embd + d] = nt > 0 ? (float)(sum[(size_t)m->emb_layer[k] * m->n_embd + d] / nt) : 0; brain_capture_end(b); } free(prompt); if (n <= 0) return false; if (!vec) return true; const float *act = brain_last_activation(b); for (int l = 0; l < m->n_layer; l++) for (int q = 0; q < m->per_layer; q++) { double s = 0; int cnt = 0; for (int j = q * MEM_GROUP; j < (q + 1) * MEM_GROUP && j < info.n_ff; j++, cnt++) s += fabsf(act[(size_t)l * info.n_ff + j]); vec[l * m->per_layer + q] = (float)(s / cnt); } return true; } bool mem_activation(MemoryStore *m, Brain *b, const char *text, float *vec, float *emb) { if (!read_text(m, b, text, vec, emb)) return false; if (emb) normalize_emb(m, emb); return true; } void mem_set_background(MemoryStore *m, Brain *b, const Atlas *a) { int total = 0; for (int c = 0; c < a->n_cat; c++) total += a->cat[c].n_prompts; if (total < 2) return; float *all = malloc(sizeof(float) * (size_t)total * m->n_groups); float *eall = malloc(sizeof(float) * (size_t)total * m->emb_dim); int n = 0; for (int c = 0; c < a->n_cat; c++) for (int p = 0; p < a->cat[c].n_prompts; p++) if (read_text(m, b, a->cat[c].prompts[p], all + (size_t)n * m->n_groups, eall + (size_t)n * m->emb_dim)) n++; if (n < 2) { free(all); free(eall); return; } free(m->emb_mu); free(m->emb_sd); m->emb_mu = calloc((size_t)m->emb_dim, sizeof(float)); m->emb_sd = calloc((size_t)m->emb_dim, sizeof(float)); for (int d = 0; d < m->emb_dim; d++) { double s = 0, s2 = 0; for (int i = 0; i < n; i++) { double v = eall[(size_t)i * m->emb_dim + d]; s += v; s2 += v * v; } double mu = s / n, var = s2 / n - mu * mu; m->emb_mu[d] = (float)mu; m->emb_sd[d] = (float)fmax(sqrt(var > 0 ? var : 0), 1e-3 + 0.05 * fabs(mu)); } free(eall); free(m->bg_mu); free(m->bg_sd); m->bg_mu = calloc((size_t)m->n_groups, sizeof(float)); m->bg_sd = calloc((size_t)m->n_groups, sizeof(float)); for (int g = 0; g < m->n_groups; g++) { double s = 0, s2 = 0; for (int i = 0; i < n; i++) { double v = all[(size_t)i * m->n_groups + g]; s += v; s2 += v * v; } double mu = s / n, var = s2 / n - mu * mu; m->bg_mu[g] = (float)mu; m->bg_sd[g] = (float)fmax(sqrt(var > 0 ? var : 0), 0.05 * mu + 1e-6); } free(all); } #define FONDO_MAGIC "NEURONAL-FONDO-1" bool mem_background_save(const MemoryStore *m, const char *path, const char *key) { if (!m->emb_mu || !m->emb_sd || !m->bg_mu || !m->bg_sd) return false; FILE *f = fopen(path, "wb"); if (!f) return false; unsigned kl = (unsigned)strlen(key); int dims[2] = {m->n_groups, m->emb_dim}; bool ok = fwrite(FONDO_MAGIC, 1, sizeof FONDO_MAGIC, f) == sizeof FONDO_MAGIC && fwrite(&kl, 4, 1, f) == 1 && fwrite(key, 1, kl, f) == kl && fwrite(dims, sizeof dims, 1, f) == 1 && fwrite(m->emb_mu, sizeof(float), (size_t)m->emb_dim, f) == (size_t)m->emb_dim && fwrite(m->emb_sd, sizeof(float), (size_t)m->emb_dim, f) == (size_t)m->emb_dim && fwrite(m->bg_mu, sizeof(float), (size_t)m->n_groups, f) == (size_t)m->n_groups && fwrite(m->bg_sd, sizeof(float), (size_t)m->n_groups, f) == (size_t)m->n_groups; ok = fclose(f) == 0 && ok; if (!ok) remove(path); return ok; } bool mem_background_load(MemoryStore *m, const char *path, const char *key) { FILE *f = fopen(path, "rb"); if (!f) return false; char magic[sizeof FONDO_MAGIC], k[1024]; unsigned kl = 0; int dims[2] = {0, 0}; bool ok = fread(magic, 1, sizeof magic, f) == sizeof magic && !memcmp(magic, FONDO_MAGIC, sizeof magic) && fread(&kl, 4, 1, f) == 1 && kl < sizeof k && fread(k, 1, kl, f) == kl; if (ok) { k[kl] = 0; ok = !strcmp(k, key); } ok = ok && fread(dims, sizeof dims, 1, f) == 1 && dims[0] == m->n_groups && dims[1] == m->emb_dim; if (ok) { float *emu = malloc(sizeof(float) * (size_t)m->emb_dim), *esd = malloc(sizeof(float) * (size_t)m->emb_dim); float *bmu = malloc(sizeof(float) * (size_t)m->n_groups), *bsd = malloc(sizeof(float) * (size_t)m->n_groups); ok = fread(emu, sizeof(float), (size_t)m->emb_dim, f) == (size_t)m->emb_dim && fread(esd, sizeof(float), (size_t)m->emb_dim, f) == (size_t)m->emb_dim && fread(bmu, sizeof(float), (size_t)m->n_groups, f) == (size_t)m->n_groups && fread(bsd, sizeof(float), (size_t)m->n_groups, f) == (size_t)m->n_groups; if (ok) { free(m->emb_mu); free(m->emb_sd); free(m->bg_mu); free(m->bg_sd); m->emb_mu = emu; m->emb_sd = esd; m->bg_mu = bmu; m->bg_sd = bsd; } else { free(emu); free(esd); free(bmu); free(bsd); } } fclose(f); return ok; } // Profilo normalizzato: quanto ogni gruppo si accende più (o meno) del suo solito (z-score), // poi centrato e portato a lunghezza 1. Senza riferimento, solo centrato. static const MemoryStore *CUR; static void center(float *v, int n) { if (CUR && CUR->bg_mu && n == CUR->n_groups) for (int i = 0; i < n; i++) v[i] = (v[i] - CUR->bg_mu[i]) / CUR->bg_sd[i]; double mean = 0, ss = 0; for (int i = 0; i < n; i++) mean += v[i]; mean /= n; for (int i = 0; i < n; i++) { v[i] -= (float)mean; ss += (double)v[i] * v[i]; } float inv = ss > 0 ? (float)(1.0 / sqrt(ss)) : 0; for (int i = 0; i < n; i++) v[i] *= inv; } // ---- scrittura e lettura dei bit ------------------------------------------------------------------ static bool slot_io(MemoryStore *m, int g, uint8_t *buf, bool write) { int layer = g / m->per_layer, q = g % m->per_layer; char path[1100]; layer_file(m, layer, path, sizeof path); FILE *f = fopen(path, write ? "r+b" : "rb"); if (!f) return false; bool ok = fseek(f, (long)q * MEM_SLOT, SEEK_SET) == 0 && (write ? fwrite(buf, 1, MEM_SLOT, f) : fread(buf, 1, MEM_SLOT, f)) == MEM_SLOT; fclose(f); return ok; } int mem_add(MemoryStore *m, Brain *b, const char *title, const char *text, char *err, size_t cap) { int len = (int)strlen(text); if (len == 0 || len > MEM_MAX_TEXT) { snprintf(err, cap, "il testo deve avere da 1 a %d caratteri", MEM_MAX_TEXT); return -1; } int need = (len + MEM_SLOT - 1) / MEM_SLOT; long free_groups = m->n_groups - mem_used_groups(m); if (need > free_groups) { snprintf(err, cap, "memoria piena: servono %d gruppi, liberi %ld", need, free_groups); return -1; } float *vec = malloc(sizeof(float) * (size_t)m->n_groups); float *emb = malloc(sizeof(float) * (size_t)m->emb_dim); if (!mem_activation(m, b, text, vec, emb)) { free(vec); free(emb); snprintf(err, cap, "lettura del testo fallita"); return -1; } if (m->n_mems == m->cap_mems) { m->cap_mems = m->cap_mems ? m->cap_mems * 2 : 16; m->mems = realloc(m->mems, sizeof(Memory) * (size_t)m->cap_mems); } Memory *x = &m->mems[m->n_mems]; memset(x, 0, sizeof *x); x->id = m->next_id++; snprintf(x->title, sizeof x->title, "%s", title && *title ? title : "Ricordo"); for (char *c = x->title; *c; c++) if (*c == '\t' || *c == '\n' || *c == '\r') *c = ' '; x->len = len; x->crc = crc32((const uint8_t *)text, (size_t)len); x->t = time(NULL); x->emb = emb; // impronta: i gruppi che si accendono di più rispetto al normale float *cen = malloc(sizeof(float) * (size_t)m->n_groups); memcpy(cen, vec, sizeof(float) * (size_t)m->n_groups); CUR = m; center(cen, m->n_groups); int *order = malloc(sizeof(int) * (size_t)m->n_groups); for (int g = 0; g < m->n_groups; g++) order[g] = g; // ordinamento per attivazione decrescente (selezione parziale sufficiente: servono pochi gruppi) int want = need + MEM_KEY + 256; if (want > m->n_groups) want = m->n_groups; for (int i = 0; i < want; i++) { int best = i; for (int k = i + 1; k < m->n_groups; k++) if (cen[order[k]] > cen[order[best]]) best = k; int t = order[i]; order[i] = order[best]; order[best] = t; } for (int k = 0; k < MEM_KEY && k < m->n_groups; k++) { x->key_g[k] = order[k]; x->key_w[k] = cen[order[k]]; x->n_key++; } // casa dei byte: i gruppi liberi più accesi (poi, se servono, qualsiasi gruppo libero) x->groups = malloc(sizeof(int) * (size_t)need); for (int i = 0; i < m->n_groups && x->n_groups < need; i++) { int g = i < want ? order[i] : -1; if (g < 0) break; if (!m->owner[g]) x->groups[x->n_groups++] = g; } for (int g = 0; g < m->n_groups && x->n_groups < need; g++) { bool used = m->owner[g] != 0; for (int k = 0; k < x->n_groups && !used; k++) used = x->groups[k] == g; if (!used) x->groups[x->n_groups++] = g; } bool ok = true; for (int k = 0; k < x->n_groups && ok; k++) { uint8_t slot[MEM_SLOT] = {0}; int from = k * MEM_SLOT, n = len - from < MEM_SLOT ? len - from : MEM_SLOT; memcpy(slot, text + from, (size_t)n); ok = slot_io(m, x->groups[k], slot, true); } free(vec); free(cen); free(order); if (!ok) { free(x->groups); free(x->emb); snprintf(err, cap, "scrittura sul disco fallita"); return -1; } for (int k = 0; k < x->n_groups; k++) m->owner[x->groups[k]] = x->id; m->n_mems++; save_emb(m, x); save_map(m); return x->id; } bool mem_delete(MemoryStore *m, int id) { for (int i = 0; i < m->n_mems; i++) { Memory *x = &m->mems[i]; if (x->id != id) continue; uint8_t zero[MEM_SLOT] = {0}; for (int k = 0; k < x->n_groups; k++) { slot_io(m, x->groups[k], zero, true); m->owner[x->groups[k]] = 0; } char path[1200]; emb_path(m, x->id, path, sizeof path); remove(path); free(x->groups); free(x->emb); m->mems[i] = m->mems[--m->n_mems]; save_map(m); return true; } return false; } char *mem_read(MemoryStore *m, const Memory *x) { char *out = malloc((size_t)x->n_groups * MEM_SLOT + 1); for (int k = 0; k < x->n_groups; k++) if (!slot_io(m, x->groups[k], (uint8_t *)out + (size_t)k * MEM_SLOT, false)) { free(out); return NULL; } out[x->len] = 0; if (crc32((const uint8_t *)out, (size_t)x->len) != x->crc) { free(out); return NULL; } // bit danneggiati return out; } int mem_recall(MemoryStore *m, const float *qemb, int *ids, float *scores, int max) { int n = 0; for (int i = 0; i < m->n_mems; i++) { const Memory *x = &m->mems[i]; if (!x->emb) continue; double dot = 0; for (int d = 0; d < m->emb_dim; d++) dot += (double)x->emb[d] * qemb[d]; float sc = (float)dot; // coseno tra le due impronte int pos = n < max ? n++ : max - 1; if (pos == max - 1 && n == max && sc <= scores[pos]) continue; while (pos > 0 && scores[pos - 1] < sc) { scores[pos] = scores[pos - 1]; ids[pos] = ids[pos - 1]; pos--; } scores[pos] = sc; ids[pos] = x->id; } return n; } void mem_json(MemoryStore *m, SBuf *o) { long used = mem_used_groups(m); sb_printf(o, "{\"groups\":%d,\"per_layer\":%d,\"group_neurons\":%d,\"slot_bits\":%d,\"used\":%ld,\"capacity_bytes\":%ld,\"memories\":[", m->n_groups, m->per_layer, MEM_GROUP, MEM_SLOT * 8, used, (long)m->n_groups * MEM_SLOT); for (int i = 0; i < m->n_mems; i++) { const Memory *x = &m->mems[i]; char *text = mem_read(m, x); sb_printf(o, "%s{\"id\":%d,\"bytes\":%d,\"t\":%lld,\"intact\":%s,\"title\":", i ? "," : "", x->id, x->len, (long long)x->t, text ? "true" : "false"); sb_json_str(o, x->title); sb_puts(o, ",\"text\":"); sb_json_str(o, text ? text : ""); sb_puts(o, ",\"groups\":["); for (int k = 0; k < x->n_groups; k++) sb_printf(o, "%s%d", k ? "," : "", x->groups[k]); sb_puts(o, "]}"); free(text); } sb_puts(o, "]}"); }