Download engine/src/blocks.c from amogaddy/neuronal: direct link, hf CLI and curl.
- Browser
- Download file 24.8 kB
-
https://huggingface.co/spaces/amogaddy/neuronal/resolve/main/engine/src/blocks.c
- Command line
-
hf download hf://spaces/amogaddy/neuronal/engine/src/blocks.c
-
curl -L -o blocks.c https://huggingface.co/spaces/amogaddy/neuronal/resolve/main/engine/src/blocks.c
24.8 kB
| static const float TEST_GAINS[N_TEST_GAINS] = {0.5f, 1.0f, 2.0f}; | |
| void block_init(Block *b, int id, int n, const char *name) { | |
| memset(b, 0, sizeof *b); | |
| b->id = id; | |
| b->n = n < 1 ? 1 : n > BLOCK_MAX_NEURONS ? BLOCK_MAX_NEURONS : n; | |
| b->gain = 1.0f; | |
| b->enabled = true; | |
| b->first = b->last = -1; | |
| if (name && *name) snprintf(b->name, sizeof b->name, "%s", name); | |
| else snprintf(b->name, sizeof b->name, "Blocco %d", id); | |
| } | |
| static void free_weights(Block *b) { | |
| free(b->area.layer); free(b->area.w_in); free(b->area.w_out); free(b->area.bias); free(b->area.scale); | |
| free(b->links_out); free(b->links_in); | |
| memset(&b->area, 0, sizeof b->area); | |
| b->links_out = b->links_in = NULL; | |
| } | |
| void block_free(Block *b) { | |
| free_weights(b); | |
| for (int i = 0; i < b->n_examples; i++) free(b->examples[i]); | |
| b->n_examples = 0; | |
| } | |
| // ---- testo dal modello ---------------------------------------------------------- | |
| static char *trim(char *s) { | |
| while (isspace((unsigned char)*s)) s++; | |
| char *e = s + strlen(s); | |
| while (e > s && isspace((unsigned char)e[-1])) *--e = 0; | |
| return s; | |
| } | |
| // Toglie numerazione, trattini e virgolette all'inizio e alla fine di una riga. | |
| static char *clean_line(char *s) { | |
| s = trim(s); | |
| while (*s && (isdigit((unsigned char)*s) || strchr("-*.)#:", *s) || isspace((unsigned char)*s))) s++; | |
| const char *quotes[] = {"\"", "«", "“", "'"}; | |
| for (size_t q = 0; q < 4; q++) if (!strncmp(s, quotes[q], strlen(quotes[q]))) s += strlen(quotes[q]); | |
| s = trim(s); | |
| const char *ends[] = {"\"", "»", "”"}; | |
| for (size_t q = 0; q < 3; q++) { | |
| size_t n = strlen(s), m = strlen(ends[q]); | |
| if (n >= m && !strcmp(s + n - m, ends[q])) s[n - m] = 0; | |
| } | |
| return trim(s); | |
| } | |
| static int ask(Brain *brain, const char *system_prompt, const char *question, float temp, unsigned seed, | |
| int max_tokens, char *out, size_t cap) { | |
| ChatMsg m[2] = {{ROLE_SYSTEM, system_prompt}, {ROLE_USER, question}}; | |
| GenOpts o = {.thinking = false, .max_tokens = max_tokens, .temperature = temp, .seed = seed}; | |
| return brain_chat(brain, m, 2, &o, NULL, NULL, out, cap); | |
| } | |
| static void add_example(Block *b, const char *s) { | |
| if (b->n_examples < BLOCK_MAX_EXAMPLES) b->examples[b->n_examples++] = strdup(s); | |
| } | |
| // ---- misura --------------------------------------------------------------------- | |
| typedef struct { float *x, *o; } Capture; // medie per strato [L][E] di ingresso FFN e uscita strato | |
| static bool capture_text(Brain *brain, const char *text, int L, int E, Capture *c) { | |
| brain_capture_begin(brain); | |
| int n = brain_feed(brain, text); | |
| long nt; | |
| const double *x = brain_capture_x(brain, &nt), *o = brain_capture_out(brain); | |
| brain_capture_end(brain); | |
| if (n <= 0 || nt <= 0) return false; | |
| size_t N = (size_t)L * E; | |
| c->x = malloc(N * sizeof(float)); | |
| c->o = malloc(N * sizeof(float)); | |
| for (size_t i = 0; i < N; i++) { c->x[i] = (float)(x[i] / nt); c->o[i] = (float)(o[i] / nt); } | |
| return true; | |
| } | |
| static float dotf(const float *a, const float *b, int n) { | |
| double s = 0; | |
| for (int i = 0; i < n; i++) s += (double)a[i] * b[i]; | |
| return (float)s; | |
| } | |
| static void normalize(float *v, int n) { | |
| double s = 0; | |
| for (int i = 0; i < n; i++) s += (double)v[i] * v[i]; | |
| float inv = s > 0 ? (float)(1.0 / sqrt(s)) : 0; | |
| for (int i = 0; i < n; i++) v[i] *= inv; | |
| } | |
| // Quanto la direzione "esempi - neutri" separa i due gruppi in questo strato (d' di Cohen). | |
| static float layer_separation(const Capture *pos, int P, const Capture *neg, int N, int l, int E, float *w) { | |
| size_t off = (size_t)l * E; | |
| for (int d = 0; d < E; d++) { | |
| double mp = 0, mn = 0; | |
| for (int i = 0; i < P; i++) mp += pos[i].x[off + d]; | |
| for (int j = 0; j < N; j++) mn += neg[j].x[off + d]; | |
| w[d] = (float)(mp / P - mn / N); | |
| } | |
| normalize(w, E); | |
| double sp = 0, sp2 = 0, sn = 0, sn2 = 0; | |
| for (int i = 0; i < P; i++) { double p = dotf(w, pos[i].x + off, E); sp += p; sp2 += p * p; } | |
| for (int j = 0; j < N; j++) { double p = dotf(w, neg[j].x + off, E); sn += p; sn2 += p * p; } | |
| double mp = sp / P, mn = sn / N; | |
| double vp = sp2 / P - mp * mp, vn = sn2 / N - mn * mn; | |
| return (float)((mp - mn) / sqrt(0.5 * (vp + vn) + 1e-6)); | |
| } | |
| // ---- applicazione ----------------------------------------------------------------- | |
| bool block_apply(Block *blk, Brain *brain, Synapses *syn, const Atlas *atlas, const char *system_prompt, | |
| const char *function, char **user_examples, int n_user_examples, int fix_first, int fix_last, | |
| block_progress_fn progress, void *ud, char *err, size_t err_cap) { | |
| BrainInfo info = brain_info(brain); | |
| const int L = info.n_layer, E = info.n_embd; | |
| blk->n_embd = E; | |
| char msg[512], q[1600]; | |
| size_t cap = 8192; | |
| char *out = malloc(cap); | |
| bool ok = false; | |
| Capture *pos = NULL, *neg = NULL; | |
| int P = 0, N = 0; | |
| float *w = NULL; | |
| char question[400] = ""; | |
| brain_area_remove(brain, blk->id); // una nuova funzione sostituisce la precedente | |
| free_weights(blk); | |
| for (int i = 0; i < blk->n_examples; i++) free(blk->examples[i]); | |
| blk->n_examples = 0; | |
| blk->applied = false; | |
| snprintf(blk->function, sizeof blk->function, "%s", function); | |
| // 1. Neuronal studia la funzione: esempi, nome, domanda di prova | |
| progress(ud, "Neuronal sta studiando la funzione..."); | |
| for (int i = 0; i < n_user_examples; i++) add_example(blk, user_examples[i]); | |
| if (blk->n_examples < 6) { | |
| snprintf(q, sizeof q, | |
| "Nel tuo cervello è appena nato un blocco di %d neuroni. La sua funzione sarà: \"%s\".\n" | |
| "Scrivi 10 frasi brevi, concrete e diverse in italiano in cui questa funzione è chiaramente all'opera. " | |
| "Una frase per riga, senza numeri, elenchi o virgolette.", blk->n, function); | |
| for (int attempt = 0; attempt < 3 && blk->n_examples < 6; attempt++) { | |
| if (ask(brain, system_prompt, q, 0.7f, 7u + (unsigned)attempt, 700, out, cap) < 0) break; | |
| for (char *line = strtok(out, "\n"); line; line = strtok(NULL, "\n")) { | |
| char *s = clean_line(line); | |
| if (strlen(s) >= 15 && !text_looks_foreign(s)) add_example(blk, s); | |
| } | |
| } | |
| } | |
| if (blk->n_examples < 3) { snprintf(err, err_cap, "Neuronal non è riuscito a elaborare esempi della funzione"); goto done; } | |
| if (!strncmp(blk->name, "Blocco ", 7)) { | |
| snprintf(q, sizeof q, "Dai un nome breve (massimo 3 parole, in italiano) a un blocco di neuroni con questa funzione: \"%s\". " | |
| "Rispondi solo con il nome.", function); | |
| if (ask(brain, system_prompt, q, 0, 0, 16, out, cap) >= 0) { | |
| char *s = clean_line(strtok(out, "\n") ? out : out); | |
| size_t n = strlen(s); | |
| if (n && s[n - 1] == '.') s[n - 1] = 0; | |
| if (*s && strlen(s) < sizeof blk->name && !text_looks_foreign(s)) snprintf(blk->name, sizeof blk->name, "%s", s); | |
| } | |
| } | |
| snprintf(q, sizeof q, "Scrivi una sola domanda breve in italiano, che un utente potrebbe farti, a cui si risponde meglio " | |
| "usando questa funzione: \"%s\". Rispondi solo con la domanda.", function); | |
| if (ask(brain, system_prompt, q, 0, 0, 60, out, cap) >= 0) snprintf(question, sizeof question, "%s", clean_line(out)); | |
| // 2. lettura degli esempi e di testi neutri (due per categoria della mappa) | |
| int n_neutral = 0; | |
| for (int c = 0; c < atlas->n_cat; c++) n_neutral += atlas->cat[c].n_prompts < 2 ? atlas->cat[c].n_prompts : 2; | |
| snprintf(msg, sizeof msg, "Lettura di %d esempi e confronto con %d testi neutri...", blk->n_examples, n_neutral); | |
| progress(ud, msg); | |
| pos = calloc((size_t)blk->n_examples, sizeof(Capture)); | |
| neg = calloc((size_t)n_neutral, sizeof(Capture)); | |
| for (int i = 0; i < blk->n_examples; i++) if (capture_text(brain, blk->examples[i], L, E, &pos[P])) P++; | |
| for (int c = 0; c < atlas->n_cat; c++) | |
| for (int p = 0; p < atlas->cat[c].n_prompts && p < 2; p++) | |
| if (capture_text(brain, atlas->cat[c].prompts[p], L, E, &neg[N])) N++; | |
| if (P < 2 || N < 2) { snprintf(err, err_cap, "lettura degli esempi fallita"); goto done; } | |
| // 3. strati: finestra contigua dove la funzione si distingue meglio dai testi neutri | |
| progress(ud, "Neuronal cerca gli strati in cui la funzione vive..."); | |
| int W = blk->n / 16; | |
| W = W < 1 ? 1 : W > 6 ? 6 : W; | |
| float *sep = calloc((size_t)L, sizeof(float)); | |
| w = malloc(sizeof(float) * (size_t)E); | |
| int lo = 2, hi = L - 3; // strati ammessi | |
| if (fix_first >= 0 && fix_last >= fix_first && fix_last < L) { lo = fix_first; hi = fix_last; } | |
| if (W > hi - lo + 1) W = hi - lo + 1; | |
| for (int l = lo; l <= hi; l++) sep[l] = layer_separation(pos, P, neg, N, l, E, w); | |
| int best = lo; | |
| float best_s = -1e30f; | |
| for (int s0 = lo; s0 + W - 1 <= hi; s0++) { | |
| float s = 0; | |
| for (int l = s0; l < s0 + W; l++) s += sep[l]; | |
| if (s > best_s) { best_s = s; best = s0; } | |
| } | |
| blk->first = best; | |
| blk->last = best + W - 1; | |
| blk->separation = best_s / W; | |
| free(sep); | |
| // 4. costruzione dei neuroni: ognuno parte da un esempio (o da una miscela di due) | |
| snprintf(msg, sizeof msg, "Costruzione di %d neuroni negli strati %d-%d...", blk->n, blk->first, blk->last); | |
| progress(ud, msg); | |
| BrainArea *A = &blk->area; | |
| A->n = blk->n; | |
| A->layer = malloc(sizeof(int) * (size_t)blk->n); | |
| A->bias = malloc(sizeof(float) * (size_t)blk->n); | |
| A->scale = malloc(sizeof(float) * (size_t)blk->n); | |
| A->w_in = malloc(sizeof(float) * (size_t)blk->n * E); | |
| A->w_out = malloc(sizeof(float) * (size_t)blk->n * E); | |
| A->enabled = true; | |
| A->tonic = false; | |
| int *base = malloc(sizeof(int) * (size_t)blk->n); // esempio principale di ogni neurone | |
| float *mix_x = malloc(sizeof(float) * (size_t)E); | |
| double pos_sum = 0, neg_sum = 0; | |
| for (int k = 0; k < blk->n; k++) { | |
| int l = blk->first + k % W; | |
| int e1 = (k / W) % P, round = k / (W * P); | |
| int e2 = P > 1 ? (e1 + round) % P : e1; | |
| float alpha = round == 0 ? 1.0f : 0.35f + 0.3f * (float)((round * 37) % 10) / 10.0f; | |
| int per_layer = blk->n / W + (k % W < blk->n % W); | |
| size_t off = (size_t)l * E; | |
| float *wi = A->w_in + (size_t)k * E, *wo = A->w_out + (size_t)k * E; | |
| for (int d = 0; d < E; d++) { | |
| double mn = 0, mo = 0, mp = 0; | |
| for (int j = 0; j < N; j++) { mn += neg[j].x[off + d]; mo += neg[j].o[off + d]; } | |
| for (int i = 0; i < P; i++) mp += pos[i].x[off + d]; | |
| mn /= N; mo /= N; mp /= P; | |
| mix_x[d] = alpha * pos[e1].x[off + d] + (1 - alpha) * pos[e2].x[off + d]; | |
| // direzione comune della funzione + la sfumatura del proprio esempio | |
| wi[d] = (float)(0.7 * (mp - mn) + 0.3 * (mix_x[d] - mp)); | |
| float o = alpha * pos[e1].o[off + d] + (1 - alpha) * pos[e2].o[off + d]; | |
| wo[d] = (float)((o - mo) / ((double)per_layer * W)); // tutto il blocco insieme ≈ una differenza piena | |
| } | |
| normalize(wi, E); | |
| // soglia tarata su tutti gli esempi: neutro medio -> 0, esempio medio -> 1 | |
| float pm = 0, nm = 0; | |
| for (int i = 0; i < P; i++) pm += dotf(wi, pos[i].x + off, E); | |
| for (int j = 0; j < N; j++) nm += dotf(wi, neg[j].x + off, E); | |
| pm /= P; nm /= N; | |
| A->layer[k] = l; | |
| A->bias[k] = nm + 0.15f * (pm - nm); | |
| A->scale[k] = pm > A->bias[k] ? 1.0f / (pm - A->bias[k]) : 0.0f; | |
| base[k] = e1; | |
| for (int i = 0; i < P; i++) { | |
| float a = (dotf(wi, pos[i].x + off, E) - A->bias[k]) * A->scale[k]; | |
| pos_sum += a < 0 ? 0 : a > 1 ? 1 : a; | |
| } | |
| for (int j = 0; j < N; j++) { | |
| float a = (dotf(wi, neg[j].x + off, E) - A->bias[k]) * A->scale[k]; | |
| neg_sum += a < 0 ? 0 : a > 1 ? 1 : a; | |
| } | |
| } | |
| blk->pos_rate = (float)(pos_sum / ((double)blk->n * P)); | |
| blk->neg_rate = (float)(neg_sum / ((double)blk->n * N)); | |
| free(mix_x); | |
| // 5. sinapsi: per ogni strato, una passata sulle matrici con i neuroni "base" (uno per esempio) | |
| progress(ud, "Collegamento del blocco alle sinapsi della rete..."); | |
| blk->links_out = calloc((size_t)blk->n * BLOCK_LINKS, sizeof(SynLink)); | |
| blk->links_in = calloc((size_t)blk->n * BLOCK_LINKS, sizeof(SynLink)); | |
| if (syn) { | |
| float *vo = malloc(sizeof(float) * (size_t)P * E), *vi = malloc(sizeof(float) * (size_t)P * E); | |
| SynLink *lo = malloc(sizeof(SynLink) * (size_t)P * BLOCK_LINKS), *li = malloc(sizeof(SynLink) * (size_t)P * BLOCK_LINKS); | |
| int *rep = malloc(sizeof(int) * (size_t)P); // neurone rappresentante di ogni esempio nello strato | |
| for (int l = blk->first; l <= blk->last; l++) { | |
| int nb = 0; | |
| for (int e = 0; e < P; e++) { | |
| rep[e] = -1; | |
| for (int k = 0; k < blk->n; k++) if (A->layer[k] == l && base[k] == e) { rep[e] = k; break; } | |
| if (rep[e] < 0) continue; | |
| memcpy(vo + (size_t)nb * E, A->w_out + (size_t)rep[e] * E, sizeof(float) * (size_t)E); | |
| memcpy(vi + (size_t)nb * E, A->w_in + (size_t)rep[e] * E, sizeof(float) * (size_t)E); | |
| rep[nb++] = rep[e]; | |
| } | |
| syn_vectors_out(syn, l, vo, nb, BLOCK_LINKS, lo); | |
| syn_vectors_in(syn, l, vi, nb, BLOCK_LINKS, li); | |
| for (int k = 0; k < blk->n; k++) { | |
| if (A->layer[k] != l) continue; | |
| for (int r = 0; r < nb; r++) if (base[rep[r]] == base[k]) { | |
| memcpy(blk->links_out + (size_t)k * BLOCK_LINKS, lo + (size_t)r * BLOCK_LINKS, sizeof(SynLink) * BLOCK_LINKS); | |
| memcpy(blk->links_in + (size_t)k * BLOCK_LINKS, li + (size_t)r * BLOCK_LINKS, sizeof(SynLink) * BLOCK_LINKS); | |
| break; | |
| } | |
| } | |
| } | |
| free(vo); free(vi); free(lo); free(li); free(rep); | |
| } | |
| free(base); | |
| // 6. Neuronal prova il blocco a diverse intensità e sceglie come usarlo | |
| blk->gain = TEST_GAINS[1]; | |
| blk->applied = true; | |
| blk->enabled = true; | |
| snprintf(blk->effect, sizeof blk->effect, "Nessuna prova eseguita."); | |
| if (*question) { | |
| progress(ud, "Neuronal prova il blocco a diverse intensità..."); | |
| char answers[N_TEST_GAINS + 1][360]; | |
| for (int g = 0; g <= N_TEST_GAINS; g++) { // l'ultima prova è a blocco spento | |
| A->gain = g < N_TEST_GAINS ? TEST_GAINS[g] : 0.0f; | |
| A->enabled = g < N_TEST_GAINS; | |
| brain_area_remove(brain, blk->id); | |
| brain_area_add(brain, blk->id, A); | |
| answers[g][0] = 0; | |
| if (ask(brain, system_prompt, question, 0, 0, 70, out, cap) >= 0) { | |
| char *s = trim(out); | |
| for (char *c = s; *c; c++) if (*c == '\n') *c = ' '; | |
| snprintf(answers[g], sizeof answers[g], "%s", s); | |
| } | |
| } | |
| SBuf j = {0}; | |
| sb_printf(&j, "Domanda: \"%s\"\nRisposte possibili:\n", question); | |
| for (int g = 0; g <= N_TEST_GAINS; g++) sb_printf(&j, "%c) %s\n", 'A' + g, answers[g]); | |
| sb_printf(&j, "\nQuale risposta mostra meglio questa capacità: \"%s\", restando in italiano corretto e sensata? " | |
| "Rispondi solo con la lettera.", function); | |
| int pick = 1; | |
| if (ask(brain, system_prompt, j.p, 0, 0, 4, out, cap) >= 0) { | |
| for (char *c = out; *c; c++) { | |
| int g = toupper((unsigned char)*c) - 'A'; | |
| if (g >= 0 && g <= N_TEST_GAINS && (c == out || !isalpha((unsigned char)c[-1])) && !isalpha((unsigned char)c[1])) { pick = g; break; } | |
| } | |
| } | |
| sb_free(&j); | |
| if (pick < N_TEST_GAINS && text_looks_foreign(answers[pick])) pick = 1; | |
| if (pick == N_TEST_GAINS) { | |
| blk->gain = TEST_GAINS[0]; | |
| snprintf(blk->effect, sizeof blk->effect, | |
| "Prova: \"%s\". Neuronal ha preferito la risposta senza blocco, quindi lo usa con delicatezza (x%.1f). " | |
| "Risposta scelta: \"%.200s\"", question, blk->gain, answers[pick]); | |
| } else { | |
| blk->gain = TEST_GAINS[pick]; | |
| snprintf(blk->effect, sizeof blk->effect, | |
| "Prova: \"%s\". Con il blocco a x%.1f: \"%.180s\" | senza blocco: \"%.180s\"", | |
| question, blk->gain, answers[pick], answers[N_TEST_GAINS]); | |
| } | |
| } | |
| A->gain = blk->gain; | |
| A->enabled = blk->enabled; | |
| brain_area_remove(brain, blk->id); | |
| brain_area_add(brain, blk->id, A); | |
| ok = true; | |
| done: | |
| for (int i = 0; i < P; i++) { free(pos[i].x); free(pos[i].o); } | |
| for (int j = 0; j < N; j++) { free(neg[j].x); free(neg[j].o); } | |
| free(pos); free(neg); free(out); free(w); | |
| return ok; | |
| } | |
| // ---- salvataggio ------------------------------------------------------------------ | |
| // Un file binario per blocco: <dir>/block_<id>.bin | |
| static void path_of(const char *dir, int id, char *out, size_t cap) { snprintf(out, cap, "%s/block_%d.bin", dir, id); } | |
| bool block_save(const Block *b, const char *dir) { | |
| make_dir(dir); | |
| char path[1024]; | |
| path_of(dir, b->id, path, sizeof path); | |
| FILE *f = fopen(path, "wb"); | |
| if (!f) return false; | |
| uint32_t magic = BLOCK_MAGIC; | |
| int32_t hdr[7] = {b->id, b->n, b->applied, b->enabled, b->first, b->last, b->n_embd}; | |
| float fl[4] = {b->gain, b->separation, b->pos_rate, b->neg_rate}; | |
| fwrite(&magic, 4, 1, f); | |
| fwrite(hdr, sizeof hdr, 1, f); | |
| fwrite(fl, sizeof fl, 1, f); | |
| int32_t region = b->region; | |
| fwrite(®ion, 4, 1, f); | |
| fwrite(b->name, sizeof b->name, 1, f); | |
| fwrite(b->function, sizeof b->function, 1, f); | |
| fwrite(b->effect, sizeof b->effect, 1, f); | |
| int32_t ne = b->n_examples; | |
| fwrite(&ne, 4, 1, f); | |
| for (int i = 0; i < ne; i++) { | |
| uint32_t len = (uint32_t)strlen(b->examples[i]); | |
| fwrite(&len, 4, 1, f); | |
| fwrite(b->examples[i], 1, len, f); | |
| } | |
| if (b->applied) { | |
| size_t n = (size_t)b->n, E = (size_t)b->n_embd; | |
| fwrite(b->area.layer, sizeof(int), n, f); | |
| fwrite(b->area.bias, sizeof(float), n, f); | |
| fwrite(b->area.scale, sizeof(float), n, f); | |
| fwrite(b->area.w_in, sizeof(float), n * E, f); | |
| fwrite(b->area.w_out, sizeof(float), n * E, f); | |
| fwrite(b->links_out, sizeof(SynLink), n * BLOCK_LINKS, f); | |
| fwrite(b->links_in, sizeof(SynLink), n * BLOCK_LINKS, f); | |
| } | |
| bool ok = !ferror(f); | |
| fclose(f); | |
| return ok; | |
| } | |
| static bool load_one(const char *path, int n_embd, Block *b) { | |
| FILE *f = fopen(path, "rb"); | |
| if (!f) return false; | |
| uint32_t magic = 0; | |
| int32_t hdr[7]; | |
| float fl[4]; | |
| memset(b, 0, sizeof *b); | |
| int32_t region = 0; | |
| bool ok = fread(&magic, 4, 1, f) == 1 && (magic == BLOCK_MAGIC || magic == BLOCK_MAGIC_V2) && | |
| fread(hdr, sizeof hdr, 1, f) == 1 && fread(fl, sizeof fl, 1, f) == 1 && | |
| (magic == BLOCK_MAGIC_V2 || fread(®ion, 4, 1, f) == 1) && fread(b->name, sizeof b->name, 1, f) == 1 && | |
| fread(b->function, sizeof b->function, 1, f) == 1 && fread(b->effect, sizeof b->effect, 1, f) == 1; | |
| if (ok) { | |
| b->id = hdr[0]; b->n = hdr[1]; b->applied = hdr[2]; b->enabled = hdr[3]; | |
| b->first = hdr[4]; b->last = hdr[5]; b->n_embd = hdr[6]; | |
| b->gain = fl[0]; b->separation = fl[1]; b->pos_rate = fl[2]; b->neg_rate = fl[3]; | |
| b->region = region; | |
| b->name[sizeof b->name - 1] = b->function[sizeof b->function - 1] = b->effect[sizeof b->effect - 1] = 0; | |
| ok = b->n > 0 && b->n <= BLOCK_MAX_NEURONS && (!b->applied || b->n_embd == n_embd); | |
| } | |
| int32_t ne = 0; | |
| if (ok) ok = fread(&ne, 4, 1, f) == 1 && ne >= 0 && ne <= BLOCK_MAX_EXAMPLES; | |
| for (int i = 0; ok && i < ne; i++) { | |
| uint32_t len = 0; | |
| ok = fread(&len, 4, 1, f) == 1 && len < 4096; | |
| if (!ok) break; | |
| char *s = malloc(len + 1); | |
| ok = fread(s, 1, len, f) == len; | |
| s[len] = 0; | |
| b->examples[b->n_examples++] = s; | |
| } | |
| if (ok && b->applied) { | |
| size_t n = (size_t)b->n, E = (size_t)b->n_embd; | |
| BrainArea *A = &b->area; | |
| A->n = b->n; | |
| A->layer = malloc(sizeof(int) * n); | |
| A->bias = malloc(sizeof(float) * n); | |
| A->scale = malloc(sizeof(float) * n); | |
| A->w_in = malloc(sizeof(float) * n * E); | |
| A->w_out = malloc(sizeof(float) * n * E); | |
| b->links_out = malloc(sizeof(SynLink) * n * BLOCK_LINKS); | |
| b->links_in = malloc(sizeof(SynLink) * n * BLOCK_LINKS); | |
| ok = fread(A->layer, sizeof(int), n, f) == n && fread(A->bias, sizeof(float), n, f) == n && | |
| fread(A->scale, sizeof(float), n, f) == n && fread(A->w_in, sizeof(float), n * E, f) == n * E && | |
| fread(A->w_out, sizeof(float), n * E, f) == n * E && | |
| fread(b->links_out, sizeof(SynLink), n * BLOCK_LINKS, f) == n * BLOCK_LINKS && | |
| fread(b->links_in, sizeof(SynLink), n * BLOCK_LINKS, f) == n * BLOCK_LINKS; | |
| A->gain = b->gain; | |
| A->enabled = b->enabled; | |
| } | |
| fclose(f); | |
| if (!ok) block_free(b); | |
| return ok; | |
| } | |
| bool block_file_ok(const char *path, int n_embd) { | |
| Block b; | |
| if (!load_one(path, n_embd, &b)) return false; | |
| block_free(&b); | |
| return true; | |
| } | |
| int block_load_all(const char *dir, int n_embd, Block **out) { | |
| *out = NULL; | |
| int n = 0, cap = 0; | |
| char path[1024]; | |
| for (int id = 1; id <= 4096; id++) { // gli id sono piccoli e crescenti | |
| path_of(dir, id, path, sizeof path); | |
| FILE *probe = fopen(path, "rb"); | |
| if (!probe) continue; | |
| fclose(probe); | |
| if (n == cap) { cap = cap ? cap * 2 : 8; *out = realloc(*out, sizeof(Block) * (size_t)cap); } | |
| if (load_one(path, n_embd, &(*out)[n])) n++; | |
| else fprintf(stderr, "blocco non leggibile, ignorato: %s\n", path); | |
| } | |
| return n; | |
| } | |
| void block_delete_file(const char *dir, int id) { | |
| char path[1024]; | |
| path_of(dir, id, path, sizeof path); | |
| remove(path); | |
| } | |
| void block_json(const Block *b, SBuf *o, bool links) { | |
| sb_printf(o, "{\"id\":%d,\"region\":%d,\"name\":", b->id, b->region); | |
| sb_json_str(o, b->name); | |
| sb_puts(o, ",\"function\":"); | |
| sb_json_str(o, b->function); | |
| sb_printf(o, ",\"neurons\":%d,\"applied\":%s,\"enabled\":%s,\"gain\":%.2f,\"first\":%d,\"last\":%d," | |
| "\"separation\":%.2f,\"pos_rate\":%.2f,\"neg_rate\":%.2f,\"effect\":", | |
| b->n, b->applied ? "true" : "false", b->enabled ? "true" : "false", b->gain, b->first, b->last, | |
| b->separation, b->pos_rate, b->neg_rate); | |
| sb_json_str(o, b->effect); | |
| sb_printf(o, ",\"auto_off\":%s,\"auto_gain\":%.2f,\"forced\":%s,\"auto_reason\":", | |
| b->auto_off ? "true" : "false", b->auto_gain, b->forced ? "true" : "false"); | |
| sb_json_str(o, b->auto_reason); | |
| sb_puts(o, ",\"examples\":["); | |
| for (int i = 0; i < b->n_examples; i++) { if (i) sb_puts(o, ","); sb_json_str(o, b->examples[i]); } | |
| sb_puts(o, "]"); | |
| if (links && b->applied) { | |
| sb_puts(o, ",\"layers\":["); | |
| for (int k = 0; k < b->n; k++) sb_printf(o, "%s%d", k ? "," : "", b->area.layer[k]); | |
| sb_puts(o, "],\"links_out\":["); | |
| for (int k = 0; k < b->n * BLOCK_LINKS; k++) { | |
| const SynLink *l = &b->links_out[k]; | |
| sb_printf(o, "%s[%d,%d,%.4g]", k ? "," : "", l->layer, l->idx, l->w); | |
| } | |
| sb_puts(o, "],\"links_in\":["); | |
| for (int k = 0; k < b->n * BLOCK_LINKS; k++) { | |
| const SynLink *l = &b->links_in[k]; | |
| sb_printf(o, "%s[%d,%d,%.4g]", k ? "," : "", l->layer, l->idx, l->w); | |
| } | |
| sb_puts(o, "]"); | |
| } | |
| sb_puts(o, "}"); | |
| } | |