Pulizia codice

This commit is contained in:
Riccardo Forese 2026-02-03 14:58:24 +01:00
parent ed8080e2d6
commit aeb830bb0e
4 changed files with 5 additions and 42 deletions

View file

@ -6,7 +6,6 @@ pub const Sigmoid = struct {
}
pub fn derivative(y: f32) f32 {
// Nota: qui assumiamo che 'y' sia già il risultato della sigmoide
return y * (1.0 - y);
}
};

View file

@ -1,18 +1,16 @@
const std = @import("std");
const World = @import("env.zig").World;
const env = @import("env.zig"); // Per accedere a costanti come GRID_SIZE
const env = @import("env.zig");
const Network = @import("modular_network.zig").Network;
// --- IPERPARAMETRI ---
const GAMMA: f32 = 0.9;
const LR: f32 = 0.005; // Basso perché output lineare
const LR: f32 = 0.005;
const EPSILON_START: f32 = 1.0;
const EPSILON_END: f32 = 0.05;
const DECAY_RATE: f32 = 0.0001; // Decadimento più lento dato che abbiamo tanti step
const DECAY_RATE: f32 = 0.0001;
// Helper per trovare max e argmax
fn maxVal(slice: []const f32) f32 {
var m: f32 = -1.0e20; // Numero molto basso
var m: f32 = -1.0e20;
for (slice) |v| if (v > m) {
m = v;
};
@ -31,17 +29,14 @@ fn argmax(slice: []const f32) usize {
return idx;
}
// Export aggiornato per Multi-Formica
fn exportAntJSON(world: *World, file_path: []const u8, episode: usize, epsilon: f32) !void {
const file = try std.fs.cwd().createFile(file_path, .{});
defer file.close();
// Buffer grande per contenere le coordinate di 20 formiche
var buffer: [65536]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
// Costruiamo la lista delle formiche in JSON: [[x,y], [x,y], ...]
var ants_json = std.ArrayList(u8){};
defer ants_json.deinit(allocator);
try ants_json.appendSlice(allocator, "[");
@ -52,8 +47,6 @@ fn exportAntJSON(world: *World, file_path: []const u8, episode: usize, epsilon:
}
try ants_json.appendSlice(allocator, "]");
// Scriviamo il JSON completo
// QUI RISOLVIAMO L'ERRORE: Usiamo 'epsilon' nella stringa
const json = try std.fmt.allocPrint(allocator, "{{\n \"grid_size\": {d},\n \"food\": [{d}, {d}],\n \"ants\": {s},\n \"episode\": {d},\n \"epsilon\": {d:.3}\n}}", .{ env.GRID_SIZE, world.food_x, world.food_y, ants_json.items, episode, epsilon });
try file.writeAll(json);

View file

@ -7,17 +7,14 @@ pub const MnistData = struct {
allocator: Allocator,
pub fn init(allocator: Allocator, img_path: []const u8, lbl_path: []const u8, max_items: usize) !MnistData {
// --- 1. CARICAMENTO IMMAGINI ---
const img_file = try std.fs.cwd().openFile(img_path, .{});
defer img_file.close();
try img_file.seekTo(16);
// --- 2. CARICAMENTO ETICHETTE ---
const lbl_file = try std.fs.cwd().openFile(lbl_path, .{});
defer lbl_file.close();
try lbl_file.seekTo(8);
// --- 3. ALLOCAZIONE ---
var images: std.ArrayList([]f32) = .{};
defer images.deinit(allocator);
var labels: std.ArrayList([]f32) = .{};
@ -29,11 +26,9 @@ pub const MnistData = struct {
var i: usize = 0;
while (i < max_items) : (i += 1) {
// Leggi direttamente dal file
const bytes_read = try img_file.read(&buffer);
if (bytes_read < img_size) break;
// Leggi 1 byte per l'etichetta
const lbl_read = try lbl_file.read(&label_byte);
if (lbl_read < 1) break;

View file

@ -33,7 +33,6 @@ pub const Network = struct {
return current_input;
}
// --- QUESTA ERA LA FUNZIONE MANCANTE ---
pub fn printTopology(self: *Network) void {
std.debug.print("Architettura Rete: [Input]", .{});
for (self.layers.items) |layer| {
@ -41,7 +40,6 @@ pub const Network = struct {
}
std.debug.print("\n", .{});
}
// ---------------------------------------
pub fn train(self: *Network, input: []const f32, target: []const f32, lr: f32) !f32 {
_ = self.forward(input);
@ -49,9 +47,8 @@ pub const Network = struct {
const last_layer_idx = self.layers.items.len - 1;
const last_layer = &self.layers.items[last_layer_idx];
// Questo è il buffer iniziale degli errori
var output_errors = try self.allocator.alloc(f32, last_layer.neurons_count);
defer self.allocator.free(output_errors); // Zig pulirà questo automaticamente alla fine
defer self.allocator.free(output_errors);
var total_loss: f32 = 0.0;
for (0..last_layer.neurons_count) |i| {
@ -68,23 +65,15 @@ pub const Network = struct {
var layer = &self.layers.items[i];
const prev_input = if (i == 0) input else self.layers.items[i - 1].output;
// 1. Calcoliamo i nuovi gradienti (nuova allocazione in memoria)
const new_gradients = layer.backward(next_gradients, prev_input, lr);
// 2. CHECK ANTI-LEAK:
// Se il vecchio next_gradients NON è output_errors (che è protetto dal defer),
// allora è un buffer intermedio creato dal layer precedente. Dobbiamo distruggerlo.
if (next_gradients.ptr != output_errors.ptr) {
self.allocator.free(next_gradients);
}
// 3. Aggiorniamo il puntatore per il prossimo giro
next_gradients = new_gradients;
}
// 4. Pulizia Finale:
// L'ultimo buffer creato dal primo layer (input layer) è rimasto in mano nostra.
// Dobbiamo liberare anche quello.
if (next_gradients.ptr != output_errors.ptr) {
self.allocator.free(next_gradients);
}
@ -92,27 +81,23 @@ pub const Network = struct {
return total_loss;
}
// --- SALVATAGGIO BINARIO ---
pub fn save(self: *Network, file_path: []const u8) !void {
const file = try std.fs.cwd().createFile(file_path, .{});
defer file.close();
const MagicNumber: u64 = 0xDEADBEEF;
// Scriviamo Header
try file.writeAll(std.mem.asBytes(&MagicNumber));
const layer_count = @as(u64, self.layers.items.len);
try file.writeAll(std.mem.asBytes(&layer_count));
// Scriviamo Layers
for (self.layers.items) |layer| {
const inputs = @as(u64, layer.inputs_count);
const neurons = @as(u64, layer.neurons_count);
try file.writeAll(std.mem.asBytes(&inputs));
try file.writeAll(std.mem.asBytes(&neurons));
// Scriviamo Pesi e Bias
const weights_bytes = std.mem.sliceAsBytes(layer.weights);
try file.writeAll(weights_bytes);
@ -121,7 +106,6 @@ pub const Network = struct {
}
}
// --- CARICAMENTO BINARIO ---
pub fn load(allocator: Allocator, file_path: []const u8) !Network {
const file = try std.fs.cwd().openFile(file_path, .{});
defer file.close();
@ -131,7 +115,6 @@ pub const Network = struct {
defer if (!success) net.deinit();
// Header Check
var magic: u64 = 0;
_ = try file.readAll(std.mem.asBytes(&magic));
if (magic != 0xDEADBEEF) return error.InvalidNetworkFile;
@ -139,7 +122,6 @@ pub const Network = struct {
var layer_count: u64 = 0;
_ = try file.readAll(std.mem.asBytes(&layer_count));
// Layers Loop
var i: u64 = 0;
while (i < layer_count) : (i += 1) {
var inputs: u64 = 0;
@ -148,7 +130,6 @@ pub const Network = struct {
_ = try file.readAll(std.mem.asBytes(&inputs));
_ = try file.readAll(std.mem.asBytes(&neurons));
// SE è l'ultimo layer, niente sigmoide!
const is_last = (i == layer_count - 1);
try net.addLayer(@intCast(inputs), @intCast(neurons), 0, !is_last);
@ -165,8 +146,6 @@ pub const Network = struct {
return net;
}
// --- EXPORT JSON CON IMMAGINE INPUT ---
// Aggiungiamo il parametro 'input_snapshot' (può essere null se non vogliamo stampare nulla)
pub fn exportJSON(self: *Network, file_path: []const u8, epoch: usize, loss: f32, input_snapshot: ?[]const f32) !void {
const file = try std.fs.cwd().createFile(file_path, .{});
defer file.close();
@ -181,11 +160,9 @@ pub const Network = struct {
try Utils.print(file, "{{\n \"epoch\": {d},\n \"loss\": {d:.6},\n", .{ epoch, loss });
// SEZIONE NUOVA: Stampiamo l'array dei pixel se presente
if (input_snapshot) |pixels| {
try file.writeAll(" \"input_pixels\": [");
for (pixels, 0..) |p, idx| {
// Arrotondiamo a 2 decimali per risparmiare spazio
try Utils.print(file, "{d:.2}", .{p});
if (idx < pixels.len - 1) try file.writeAll(",");
}
@ -197,7 +174,6 @@ pub const Network = struct {
for (self.layers.items, 0..) |layer, i| {
try Utils.print(file, " {{\n \"layer_index\": {d},\n \"neurons\": {d},\n \"inputs\": {d},\n \"weights\": [", .{ i, layer.neurons_count, layer.inputs_count });
// Salviamo solo un sottoinsieme dei pesi per non intasare il file
const max_w = if (layer.weights.len > 1000) 100 else layer.weights.len;
for (layer.weights[0..max_w], 0..) |w, w_idx| {