neuronal / engine /src /blocks.c
amogaddy's picture
amogaddyofficial
claude sonnet 4.6
Neuronal
d601f7c
Raw History Blame Contribute Delete
24.8 kB
#include "blocks.h"
#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <direct.h>
#define make_dir(p) _mkdir(p)
#else
#include <sys/stat.h>
#define make_dir(p) mkdir(p, 0755)
#endif
#define N_TEST_GAINS 3
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
#define BLOCK_MAGIC_V2 0x324B4C42u // "BLK2": senza macroarea
#define BLOCK_MAGIC 0x334B4C42u // "BLK3"
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(&region, 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(&region, 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, "}");
}