#Not sure where the double free is occuring

1 messages · Page 1 of 1 (latest)

steep belfry
#

I tried to implement a basic neural network in zig. The layers part of the program has been tested and works fine. But when i wrote the logic for weights. which is basically an array of matrices(i am representing a matrix with nested ArrayList), the compiler tells me there is a double free even though i wrote deinit properly(at least i think i did) .
I am not looking for alternate ways of setting up the matrices or the network, i just want to know how the double free occured.

#
const std = @import("std");
const print = std.debug.print;

const math_ex = @import("math_ex.zig");

pub fn Hivemind() type {
    return struct {
        const Self = @This();
        name: []const u8,
        layers_count: usize,
        layers_shape: []u8,
        layers: std.ArrayList(std.ArrayList(f32)),

        weights: std.ArrayList(std.ArrayList(std.ArrayList(f32))),

        pub fn init(
            allocator: std.mem.Allocator,
            name: []const u8,
            layers_shape: []u8,
        ) !Self {
            var layers = std.ArrayList(std.ArrayList(f32)).init(allocator);
            for (layers_shape) |size| {
                var layer = std.ArrayList(f32).init(allocator);
                try layer.appendNTimes(0.0, size);
                try layers.append(layer);
            }

#

            // An array of matrices
            var weights = std.ArrayList(std.ArrayList(std.ArrayList(f32))).init(allocator);

            var i: u8 = 1;
            while (i < layers.items.len) : (i += 1) {
                // A matrix
                var weights_ji = std.ArrayList(std.ArrayList(f32)).init(allocator);

                // Row of matrix weights_ji
                var row = std.ArrayList(f32).init(allocator);

                // the num of cols of the matrix will be the length of the previous layer
                try row.appendNTimes(0.0, layers.items[i].items.len);

                //the num of rows of the matrix will be the length of the current layer
                try weights_ji.appendNTimes(row, layers.items[i - 1].items.len);

                try weights.append(weights_ji);
            }

#
            return Self{
                .name = name,
                .layers_shape = layers_shape,
                .layers_count = layers_shape.len,
                .layers = layers,
                .weights = weights,
            };
        }

        pub fn deinit(self: Self) void {
            for (self.layers.items) |layer| {
                layer.deinit();
            }
            self.layers.deinit();

            for (self.weights.items) |weights_ji| {
                for (weights_ji.items) |row| {
                    row.deinit();
                }
                weights_ji.deinit();
            }
            self.weights.deinit();
          }
      }
};
#

discord does not allow long messages, i hope that is readable

#

below is the main function

#
const std = @import("std");
const hivemind = @import("hivemind.zig");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    var layers_shape = [_]u8{ 4, 2, 2, 4, 2 };
    var first = try hivemind.Hivemind().init(gpa.allocator(), "first", &layers_shape);
    defer first.deinit();

    hivemind.printHivemind(first);
}
proper oyster
#

I think you're treating the ArrayList row wrong

#

see this like try weights_ji.appendNTimes(row, layers.items[i - 1].items.len);

#

I'm going to go out on a very small limb and say that that is not giving the behaviour you expect

#

essentially that copies the value row layers.items[i - 1].items.len times

#

but it's not a deep copy

#

so the reference to the memory that row uses is repeated

#

so when you're freeing that data in .deinit, you're freeing that memory multiple times

#

I *think* you mean something like this:

#
while (i < layers.items.len) : (i += 1) {
    var weights_ji = std.ArrayList(std.ArrayList(f32)).init(allocator);
    for (0..layers.items[i].items.len) |_| {
        var row = std.ArrayList(f32).init(allocator);
        try row.appendNTimes(0.0, layers.items[i].items.len);
        try weights_ji.append(row);
    }
    try weights.append(weights_ji);
}
#

this doesn't crash for me

#

I'm not really exactly sure what the expected output is, I just tried to remove the error

steep belfry
#

are you trying to tell that the same copy of row is being appended instead of getting n rows?

proper oyster
#

maybe

#

here's my epic diagram

#

you're copying the row multiple times but they all point to the same data, so when you deinit() the rows, the same data is freed multiple times