Fix test layer. Add tests to detect silent breakage of testLayer and regression in mapAlloc with zero-size struct fields. Add Python venv directory to .gitignore.

This commit is contained in:
Tarry Singh 2023-11-06 11:25:57 +00:00
parent 237a877a29
commit 9f4194ad97
5 changed files with 49 additions and 10 deletions

View File

@ -6,7 +6,7 @@ const runtimes = @import("runtimes");
const std = @import("std");
const stdx = @import("stdx");
const platform = @import("platform.zig");
const zml_platform = @import("platform.zig");
const pjrt = @import("pjrtx.zig");
const HostBuffer = @import("hostbuffer.zig").HostBuffer;
@ -18,6 +18,10 @@ const Target = @import("platform.zig").Target;
const available_targets = @import("platform.zig").available_targets;
const log = std.log.scoped(.@"zml/context");
test {
std.testing.refAllDecls(Context);
}
/// Every program using ZML must start with a `zml.Context.init(.{});`
/// The ZML context contains global state to interact with the different
/// devices available on your system.
@ -149,7 +153,7 @@ pub const Context = struct {
return platform_ orelse @panic("No platform found !");
}
pub fn printAvailablePlatforms(self: Context, selected: platform.Platform) void {
pub fn printAvailablePlatforms(self: Context, selected: Platform) void {
// List available targets
log.info("Available Platforms:", .{});
const selected_prefix = "";
@ -157,7 +161,7 @@ pub const Context = struct {
const selected_postfix = "(AUTO-SELECTED)";
const not_selected_postfix = "";
for (platform.available_targets) |target| {
for (zml_platform.available_targets) |target| {
log.info(" {s} {s} {s}", .{
if (target == selected.target) selected_prefix else not_selected_prefix,
@tagName(target),

View File

@ -241,8 +241,7 @@ pub const BaseExe = struct {
shards.appendAssumeCapacity(dev_out[i]);
}
const out_shape = self.inner.result_buffer_shapes[i];
return Buffer.fromPjrtBuffers(self.platform(), out_shape, shards.constSlice());
return Buffer.fromPjrtBuffers(self.platform, self.result_shapes[i], shards.constSlice());
}
};

View File

@ -120,9 +120,11 @@ pub fn mapAlloc(comptime cb: anytype, allocator: std.mem.Allocator, ctx: FnParam
return;
}
if (@sizeOf(ToStruct) == 0) return;
switch (type_info_to) {
.Struct => |info| inline for (info.fields) |field| {
// if (field.is_comptime) continue;
if (field.is_comptime or @sizeOf(field.type) == 0) continue;
const field_type_info = @typeInfo(field.type);
// If the field is already a pointer, we recurse with it directly, otherwise, we recurse with a pointer to the field.
switch (field_type_info) {
@ -187,6 +189,8 @@ test mapAlloc {
}
};
const Empty = struct {};
const AA = struct {
field: A,
array: [2]A,
@ -195,6 +199,7 @@ test mapAlloc {
// We want to allow conversion from comptime to runtime, because Zig type inference works like this.
comptime static_val: u8 = 8,
comptime static_slice: [2]A = .{ .{ .a = 11 }, .{ .a = 12 } },
field_with_empty: struct { A, Empty },
};
const BB = struct {
field: B,
@ -203,6 +208,7 @@ test mapAlloc {
other: u8,
static_val: u8,
static_slice: []B,
field_with_empty: struct { B, Empty },
};
const aa: AA = .{
@ -210,6 +216,7 @@ test mapAlloc {
.array = .{ .{ .a = 5 }, .{ .a = 6 } },
.other = 7,
.slice = &.{ .{ .a = 9 }, .{ .a = 10 } },
.field_with_empty = .{ .{ .a = 9 }, .{} },
};
var bb: BB = undefined;

View File

@ -201,8 +201,8 @@ pub fn testLayerOut(
const exe = try zml.compileModel(alloc, fwd, layer, input_shapes, platform);
const n_out_exp = activations.countLayers(out_name);
if (exe.inner.result_buffer_count != n_out_exp) {
log.warn("Reference models produces {d} outputs, but implementation produces {d}", .{ n_out_exp, exe.inner.result_buffer_count });
if (exe.inner.result_shapes.len != n_out_exp) {
log.warn("Reference models produces {d} outputs, but implementation produces {d}", .{ n_out_exp, exe.inner.result_shapes.len });
}
const mod = exe.prepare(layer_weights);
@ -243,13 +243,13 @@ pub fn testLayerOut(
var buf: [1024]u8 = undefined;
var failed: bool = false;
for (0..mod.inner.result_buffer_count) |i| {
for (0..mod.inner.result_shapes.len) |i| {
const full_name = std.fmt.bufPrint(&buf, "{s}.{d}", .{ out_name, i }) catch unreachable;
const expected_out = activations.get(full_name) orelse {
log.warn("Output buffer not found: {s}", .{full_name});
continue;
};
zml.testing.expectClose(expected_out, mod.getOutputBuffer(i), tolerance) catch |err| switch (err) {
zml.testing.expectClose(expected_out, mod.inner.getOutputBuffer(i), tolerance) catch |err| switch (err) {
error.TestUnexpectedResult => {
log.err("{s}.{d} doesn't match !", .{ out_name, i });
failed = true;
@ -263,6 +263,34 @@ pub fn testLayerOut(
log.info("all good for {s} !", .{name});
}
test testLayer {
const platform = env();
// create a model
const layer: zml.nn.Linear = .{
.weight = zml.Tensor{ ._shape = zml.Shape.init(.{ 5, 2 }, .f32), ._id = .{ .buffer_id = 42 } },
};
const layer_weights: zml.Bufferized(zml.nn.Linear) = .{
.weight = try zml.Buffer.fromArray(
platform,
[5][2]f32{ .{ 0, 0 }, .{ 0, 1 }, .{ 1, 2 }, .{ -1, -1 }, .{ -1, 0 } },
),
};
// create a buffer store containing the activations:
var activations = try zml.aio.BufferStore.init(std.testing.allocator, &.{});
defer activations.deinit();
{
const input = zml.HostBuffer.fromArray(&[2]f32{ 1, -1 });
try activations.buffers.put(activations.arena.allocator(), "model.layer.in.0", input);
const output = zml.HostBuffer.fromArray(&[5]f32{ 0, -1, -1, 0, -1 });
try activations.buffers.put(activations.arena.allocator(), "model.layer.out.0", output);
}
// test the ZML layer reproduces the "captured" activations:
try zml.testing.testLayer(platform, activations, "model.layer", layer, layer_weights, 1e-5);
}
pub inline fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
return std.testing.expectEqual(expected, actual);
}

View File

@ -36,6 +36,7 @@ pub const compileFn = exe.compileFn;
pub const compileModel = exe.compileModel;
pub const FnExe = exe.FnExe;
pub const ModuleExe = exe.ModuleExe;
pub const ModuleSignature = exe.ModuleSignature;
pub const ops = @import("ops.zig");
pub const tools = struct {