Download engine/src/memory.c from amogaddy/neuronal: direct link, hf CLI and curl.
- Browser
- Download file 21.6 kB
-
https://huggingface.co/spaces/amogaddy/neuronal/resolve/main/engine/src/memory.c
- Command line
-
hf download hf://spaces/amogaddy/neuronal/engine/src/memory.c
-
curl -L -o memory.c https://huggingface.co/spaces/amogaddy/neuronal/resolve/main/engine/src/memory.c
21.6 kB
| 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); | |
| } | |
| 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, "]}"); | |
| } | |