TensorAlloc add and to compilable but still error for basic add test

This commit is contained in:
adrien 2026-05-14 22:25:35 +02:00
parent f702c1e09a
commit e6d0f62929
2 changed files with 222 additions and 128 deletions

View File

@ -3,6 +3,7 @@ const Scales = @import("Scales.zig");
const UnitScale = Scales.UnitScale; const UnitScale = Scales.UnitScale;
const Dimensions = @import("Dimensions.zig"); const Dimensions = @import("Dimensions.zig");
const Dimension = Dimensions.Dimension; const Dimension = Dimensions.Dimension;
const Allocator = std.mem.Allocator;
const sh = @import("shared.zig"); const sh = @import("shared.zig");
// //
@ -50,11 +51,11 @@ pub fn TensorAlloc(
// Specific to Alloc // Specific to Alloc
pub fn init(allocator: std.mem.Allocator) !@This() { pub fn init(allocator: Allocator) !@This() {
return .{ .data = try allocator.create(Vec) }; return .{ .data = try allocator.create(Vec) };
} }
pub fn deinit(self: @This(), allocator: std.mem.Allocator) void { pub fn deinit(self: @This(), allocator: Allocator) void {
allocator.destroy(self.data); allocator.destroy(self.data);
} }
@ -72,7 +73,7 @@ pub fn TensorAlloc(
} }
/// Broadcast a single value across all elements. /// Broadcast a single value across all elements.
pub inline fn splat(alloc: std.mem.Allocator, v: T) !Self { pub inline fn splat(alloc: Allocator, v: T) !Self {
const new = try @This().init(alloc); const new = try @This().init(alloc);
new.data.* = @splat(v); new.data.* = @splat(v);
return new; return new;
@ -88,7 +89,7 @@ pub fn TensorAlloc(
/// Element-wise add. Dimensions must match; scales resolve to finer. /// Element-wise add. Dimensions must match; scales resolve to finer.
/// RHS must have the same shape as self, or total == 1 (broadcast). /// RHS must have the same shape as self, or total == 1 (broadcast).
pub inline fn add(self: *const Self, rhs: anytype) TensorAlloc( pub inline fn add(self: *const Self, alloc: Allocator, rhs: anytype) !TensorAlloc(
T, T,
dims.argsOpt(), dims.argsOpt(),
sh.finerScales(Self, @TypeOf(rhs)).argsOpt(), sh.finerScales(Self, @TypeOf(rhs)).argsOpt(),
@ -102,10 +103,16 @@ pub fn TensorAlloc(
if (comptime RhsType.total != 1 and !sh.shapeEql(shape, RhsType.shape)) if (comptime RhsType.total != 1 and !sh.shapeEql(shape, RhsType.shape))
@compileError("Shape mismatch in add: element-wise operations require identical shapes, or a scalar RHS."); @compileError("Shape mismatch in add: element-wise operations require identical shapes, or a scalar RHS.");
var area = std.heap.ArenaAllocator.init(alloc);
defer area.deinit();
const TargetType = TensorAlloc(T, dims.argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape); const TargetType = TensorAlloc(T, dims.argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape);
const l: Vec = self.to(TargetType).data; const l: TargetType = try self.to(area.allocator(), TargetType);
const r: Vec = rhs.to(TargetType).data; const r: TargetType = try rhs.to(area.allocator(), TargetType);
return .{ .data = if (comptime sh.isInt(T)) l +| r else l + r };
const new = try TargetType.init(alloc);
new.data.* = if (comptime sh.isInt(T)) l.data.* +| r.data.* else l.data.* + r.data.*;
return new;
} }
/// Element-wise sub. Dimensions must match; scales resolve to finer. /// Element-wise sub. Dimensions must match; scales resolve to finer.
@ -226,8 +233,9 @@ pub fn TensorAlloc(
/// Scale ratio is computed fully at comptime; only a SIMD multiply at runtime. /// Scale ratio is computed fully at comptime; only a SIMD multiply at runtime.
pub inline fn to( pub inline fn to(
self: *const Self, self: *const Self,
alloc: Allocator,
comptime Dest: type, comptime Dest: type,
) Dest { ) !Dest {
if (comptime Self == Dest) return self.*; if (comptime Self == Dest) return self.*;
// Run validation checks FIRST before dealing with types // Run validation checks FIRST before dealing with types
@ -236,74 +244,85 @@ pub fn TensorAlloc(
if (comptime total != 1 and !sh.shapeEql(shape, Dest.shape)) if (comptime total != 1 and !sh.shapeEql(shape, Dest.shape))
@compileError("Shape mismatch in to: destination type must have the identical shape, or be a scalar."); @compileError("Shape mismatch in to: destination type must have the identical shape, or be a scalar.");
const vec = if (comptime total == 1 and Dest.total != 1)
TensorAlloc(Dest.ValueType, dims.argsOpt(), scales.argsOpt(), Dest.shape){ .data = @splat(self.data[0]) }
else
self;
const ratio = comptime (scales.getFactor(dims) / Dest.scales.getFactor(Dest.dims)); const ratio = comptime (scales.getFactor(dims) / Dest.scales.getFactor(Dest.dims));
const DestT = Dest.ValueType; const DestT = Dest.ValueType;
const DestVec = @Vector(Dest.total, DestT); const DestVec = @Vector(Dest.total, DestT);
if (comptime ratio == 1.0 and T == DestT)
return .{ .data = vec.data };
// If ratio is 1, handle type conversion correctly based on BOTH source and dest types
if (comptime ratio == 1.0) {
const T_info = @typeInfo(T); const T_info = @typeInfo(T);
const Dest_info = @typeInfo(DestT); const Dest_info = @typeInfo(DestT);
return .{ const vec = try TensorAlloc(Dest.ValueType, dims.argsOpt(), scales.argsOpt(), Dest.shape).splat(
.data = if (comptime T_info == .int and Dest_info == .int) alloc,
@as(DestVec, @intCast(vec.data)) if (comptime T_info == .int and Dest_info == .int)
@as(DestT, @intCast(self.data[0]))
else if (comptime T_info == .float and Dest_info == .float) else if (comptime T_info == .float and Dest_info == .float)
@as(DestVec, @floatCast(vec.data)) @as(DestT, @floatCast(self.data[0]))
else if (comptime T_info == .int and Dest_info == .float) else if (comptime T_info == .int and Dest_info == .float)
@as(DestVec, @floatFromInt(vec.data)) @as(DestT, @floatFromInt(self.data[0]))
else if (comptime T_info == .float and Dest_info == .int) else if (comptime T_info == .float and Dest_info == .int)
@as(DestVec, @intFromFloat(vec.data)) @as(DestT, @intFromFloat(self.data[0]))
else else
unreachable, unreachable,
}; );
} if (comptime total != 1 and total == Dest.total)
vec.data.* = if (comptime T_info == .int and Dest_info == .int)
@as(DestVec, @intCast(self.data.*))
else if (comptime T_info == .float and Dest_info == .float)
@as(DestVec, @floatCast(self.data.*))
else if (comptime T_info == .int and Dest_info == .float)
@as(DestVec, @floatFromInt(self.data.*))
else if (comptime T_info == .float and Dest_info == .int)
@as(DestVec, @intFromFloat(self.data.*))
else
unreachable;
defer vec.deinit(alloc);
const new = try Dest.init(alloc);
if (comptime ratio == 1.0)
return new;
if (comptime T == DestT) { if (comptime T == DestT) {
if (comptime @typeInfo(T) == .float) if (comptime @typeInfo(T) == .float) {
return .{ .data = vec.data * @as(DestVec, @splat(@as(T, @floatCast(ratio)))) }; new.data.* = vec.data.* * @as(DestVec, @splat(@as(T, @floatCast(ratio))));
return new;
}
if (comptime ratio >= 1.0) { if (comptime ratio >= 1.0) {
const mult: T = comptime @intFromFloat(@round(ratio)); const mult: T = comptime @intFromFloat(@round(ratio));
return .{ .data = vec.data *| @as(Vec, @splat(mult)) }; new.data.* = vec.data.* *| @as(Vec, @splat(mult));
} else { } else {
const div_val: T = comptime @intFromFloat(@round(1.0 / ratio)); const div_val: T = comptime @intFromFloat(@round(1.0 / ratio));
const half: T = comptime @divTrunc(div_val, 2); const half: T = comptime @divTrunc(div_val, 2);
if (comptime @typeInfo(T).int.signedness == .unsigned) { if (comptime @typeInfo(T).int.signedness == .unsigned) {
return .{ .data = @divTrunc(vec.data + @as(Vec, @splat(half)), @as(Vec, @splat(div_val))) }; new.data.* = @divTrunc(vec.data.* + @as(Vec, @splat(half)), @as(Vec, @splat(div_val)));
} else { } else {
// Vectorized branchless negative handling const is_pos = self.data.* >= @as(Vec, @splat(0));
const is_pos = self.data >= @as(Vec, @splat(0));
const offsets = @select(T, is_pos, @as(Vec, @splat(half)), @as(Vec, @splat(-half))); const offsets = @select(T, is_pos, @as(Vec, @splat(half)), @as(Vec, @splat(-half)));
return .{ .data = @divTrunc(vec.data + offsets, @as(Vec, @splat(div_val))) }; new.data.* = @divTrunc(vec.data.* + offsets, @as(Vec, @splat(div_val)));
} }
} }
return new;
} }
// Cross-type fully vectorized casting with scales // Cross-type fully vectorized casting with scales
const FVec = @Vector(total, f64); const FVec = @Vector(total, f64);
const float_vec: FVec = switch (comptime @typeInfo(T)) { const float_vec: FVec = switch (comptime @typeInfo(T)) {
.float => @floatCast(vec.data), .float => @floatCast(vec.data.*),
.int => @floatFromInt(vec.data), .int => @floatFromInt(vec.data.*),
else => unreachable, else => unreachable,
}; };
const scaled = float_vec * @as(FVec, @splat(ratio)); const scaled = float_vec * @as(FVec, @splat(ratio));
return switch (comptime @typeInfo(DestT)) { new.data.* = switch (comptime @typeInfo(DestT)) {
.float => .{ .data = @floatCast(scaled) }, .float => @floatCast(scaled),
.int => .{ .data = @intFromFloat(@round(scaled)) }, .int => @intFromFloat(@round(scaled)),
else => unreachable, else => unreachable,
}; };
return new;
} }
const CmpResult = if (total == 1) bool else [total]bool; const CmpResult = if (total == 1) bool else [total]bool;
@ -313,7 +332,7 @@ pub fn TensorAlloc(
} }
/// Resolve both sides to the finer scale, broadcasting shape {1} RHS if needed. /// Resolve both sides to the finer scale, broadcasting shape {1} RHS if needed.
inline fn resolveScalePair(self: *const Self, rhs: anytype) struct { l: Vec, r: Vec } { inline fn resolveScalePair(self: *const Self, rhs: anytype) struct { l: *Vec, r: *Vec } {
const RhsType = @TypeOf(rhs); const RhsType = @TypeOf(rhs);
if (comptime !isTensor(RhsType)) if (comptime !isTensor(RhsType))
@compileError("rhs can only be a Tensor "); @compileError("rhs can only be a Tensor ");
@ -622,28 +641,32 @@ pub fn TensorAlloc(
// Scalar tests // Scalar tests
test "Scalar initiat" { test "TensorAlloc | Scalar initiat" {
const alloc = std.testing.allocator; var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
const Meter = TensorAlloc(i128, .{ .L = 1 }, .{ .L = @enumFromInt(-3) }, &.{1}); const Meter = TensorAlloc(i128, .{ .L = 1 }, .{ .L = @enumFromInt(-3) }, &.{1});
const Second = TensorAlloc(f32, .{ .T = 1 }, .{ .T = .n }, &.{1}); const Second = TensorAlloc(f32, .{ .T = 1 }, .{ .T = .n }, &.{1});
const distance = try Meter.splat(alloc, 10); const distance = try Meter.splat(alloc, 10);
defer distance.deinit(alloc);
const time = try Second.splat(alloc, 2); const time = try Second.splat(alloc, 2);
defer time.deinit(alloc);
try std.testing.expectEqual(10, distance.data[0]); try std.testing.expectEqual(10, distance.data[0]);
try std.testing.expectEqual(2, time.data[0]); try std.testing.expectEqual(2, time.data[0]);
} }
// test "Scalar comparisons (eq, ne, gt, gte, lt, lte)" { // test "TensorAlloc | Scalar comparisons (eq, ne, gt, gte, lt, lte)" {
// var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// defer arena.deinit();
// const alloc = arena.allocator();
//
// const Meter = TensorAlloc(i128, .{ .L = 1 }, .{}, &.{1}); // const Meter = TensorAlloc(i128, .{ .L = 1 }, .{}, &.{1});
// const KiloMeter = TensorAlloc(i128, .{ .L = 1 }, .{ .L = .k }, &.{1}); // const KiloMeter = TensorAlloc(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
// //
// const m1000 = Meter.splat(1000); // const m1000 = try Meter.splat(alloc, 1000);
// const km1 = KiloMeter.splat(1); // const km1 = try KiloMeter.splat(alloc, 1);
// const km2 = KiloMeter.splat(2); // const km2 = try KiloMeter.splat(alloc, 2);
// //
// try std.testing.expect(m1000.eq(km1)); // try std.testing.expect(m1000.eq(km1));
// try std.testing.expect(km1.eq(m1000)); // try std.testing.expect(km1.eq(m1000));
@ -659,30 +682,34 @@ test "Scalar initiat" {
// try std.testing.expect(km1.lte(m1000)); // try std.testing.expect(km1.lte(m1000));
// try std.testing.expect(m1000.lte(km2)); // try std.testing.expect(m1000.lte(km2));
// } // }
//
// test "Scalar Add" { test "TensorAlloc | Scalar Add" {
// const Meter = TensorAlloc(i128, .{ .L = 1 }, .{}, &.{1}); var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// const KiloMeter = TensorAlloc(i128, .{ .L = 1 }, .{ .L = .k }, &.{1}); defer arena.deinit();
// const KiloMeter_f = TensorAlloc(f64, .{ .L = 1 }, .{ .L = .k }, &.{1}); const alloc = arena.allocator();
//
// const distance = Meter.splat(10); const Meter = TensorAlloc(i128, .{ .L = 1 }, .{}, &.{1});
// const distance2 = Meter.splat(20); const KiloMeter = TensorAlloc(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
// const added = distance.add(distance2); const KiloMeter_f = TensorAlloc(f64, .{ .L = 1 }, .{ .L = .k }, &.{1});
// try std.testing.expectEqual(30, added.data[0]);
// try std.testing.expectEqual(1, @TypeOf(added).dims.get(.L)); const distance = try Meter.splat(alloc, 10);
// const distance2 = try Meter.splat(alloc, 20);
// const distance3 = KiloMeter.splat(2); const added = try distance.add(alloc, distance2);
// const added2 = distance.add(distance3); try std.testing.expectEqual(30, added.data[0]);
// try std.testing.expectEqual(2010, added2.data[0]); try std.testing.expectEqual(1, @TypeOf(added).dims.get(.L));
//
// const added3 = distance3.add(distance).to(KiloMeter); const distance3 = try KiloMeter.splat(alloc, 2);
// try std.testing.expectEqual(2, added3.data[0]); const added2 = try distance.add(alloc, distance3);
// try std.testing.expectEqual(2010, added2.data[0]);
// const distance4 = KiloMeter_f.splat(2);
// const added4 = distance4.add(distance).to(KiloMeter_f); const added3 = try (try distance3.add(alloc, distance)).to(alloc, KiloMeter);
// try std.testing.expectApproxEqAbs(2.01, added4.data[0], 0.000001); try std.testing.expectEqual(2, added3.data[0]);
// }
// const distance4 = try KiloMeter_f.splat(alloc, 2);
const added4 = try (try distance4.add(alloc, distance)).to(alloc, KiloMeter_f);
try std.testing.expectApproxEqAbs(2.01, added4.data[0], 0.000001);
}
// test "Scalar Sub" { // test "Scalar Sub" {
// const Meter = TensorAlloc(i128, .{ .L = 1 }, .{}, &.{1}); // const Meter = TensorAlloc(i128, .{ .L = 1 }, .{}, &.{1});
// const KiloMeter_f = TensorAlloc(f64, .{ .L = 1 }, .{ .L = .k }, &.{1}); // const KiloMeter_f = TensorAlloc(f64, .{ .L = 1 }, .{ .L = .k }, &.{1});
@ -1129,3 +1156,70 @@ test "Scalar initiat" {
// try std.testing.expectEqual(4, T3.strides_arr[1]); // try std.testing.expectEqual(4, T3.strides_arr[1]);
// try std.testing.expectEqual(1, T3.strides_arr[2]); // try std.testing.expectEqual(1, T3.strides_arr[2]);
// } // }
//
// test "Slice 1D negative start" {
// const Vec = TensorStatic(i32, .{}, .{}, &.{5});
// const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
// const s = v.slice(.{.{ .start = -3, .end = 5 }}); // [2,5) 30,40,50
// try std.testing.expectEqual(3, @TypeOf(s).total);
// try std.testing.expectEqual(30, s.data[0]);
// try std.testing.expectEqual(40, s.data[1]);
// try std.testing.expectEqual(50, s.data[2]);
// }
//
// test "Slice 1D negative end" {
// const Vec = TensorStatic(i32, .{}, .{}, &.{5});
// const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
// const s = v.slice(.{.{ .start = 1, .end = -1 }}); // [1,4) 20,30,40
// try std.testing.expectEqual(3, @TypeOf(s).total);
// try std.testing.expectEqual(20, s.data[0]);
// try std.testing.expectEqual(30, s.data[1]);
// try std.testing.expectEqual(40, s.data[2]);
// }
//
// test "Slice 1D both negative" {
// const Vec = TensorStatic(i64, .{}, .{}, &.{6});
// const v = Vec{ .data = .{ 5, 10, 15, 20, 25, 30 } };
// const s = v.slice(.{.{ .start = -4, .end = -1 }}); // [2,5) 15,20,25
// try std.testing.expectEqual(3, @TypeOf(s).total);
// try std.testing.expectEqual(15, s.data[0]);
// try std.testing.expectEqual(20, s.data[1]);
// try std.testing.expectEqual(25, s.data[2]);
// }
//
// test "Slice 1D null start" {
// const Vec = TensorStatic(i32, .{}, .{}, &.{5});
// const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
// const s = v.slice(.{.{ .end = -2 }}); // [:-2] 10,20,30
// try std.testing.expectEqual(3, @TypeOf(s).total);
// try std.testing.expectEqual(10, s.data[0]);
// try std.testing.expectEqual(20, s.data[1]);
// try std.testing.expectEqual(30, s.data[2]);
// }
//
// test "Slice 1D null end" {
// const Vec = TensorStatic(i32, .{}, .{}, &.{5});
// const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
// const s = v.slice(.{.{ .start = -3 }}); // [-3:] 30,40,50
// try std.testing.expectEqual(3, @TypeOf(s).total);
// try std.testing.expectEqual(30, s.data[0]);
// try std.testing.expectEqual(40, s.data[1]);
// try std.testing.expectEqual(50, s.data[2]);
// }
//
// test "Slice 2D negative & null indices" {
// const Mat = TensorStatic(i32, .{}, .{}, &.{ 4, 4 });
// const m = Mat{ .data = .{
// 1, 2, 3, 4,
// 5, 6, 7, 8,
// 9, 10, 11, 12,
// 13, 14, 15, 16,
// } };
// // last 2 rows, last 2 cols same as subblock test [2,4)x[2,4)
// const s = m.slice(.{ .{ .start = -2, .end = 4 }, .{ .start = -2 } });
// try std.testing.expectEqual(4, @TypeOf(s).total);
// try std.testing.expectEqual(11, s.data[0]);
// try std.testing.expectEqual(12, s.data[1]);
// try std.testing.expectEqual(15, s.data[2]);
// try std.testing.expectEqual(16, s.data[3]);
// }

View File

@ -669,7 +669,7 @@ pub fn TensorStatic(
// Scalar tests // Scalar tests
test "Scalar initiat" { test "TensorStatic | Scalar initiat" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = @enumFromInt(-3) }, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = @enumFromInt(-3) }, &.{1});
const Second = TensorStatic(f32, .{ .T = 1 }, .{ .T = .n }, &.{1}); const Second = TensorStatic(f32, .{ .T = 1 }, .{ .T = .n }, &.{1});
@ -680,7 +680,7 @@ test "Scalar initiat" {
try std.testing.expectEqual(2, time.data[0]); try std.testing.expectEqual(2, time.data[0]);
} }
test "Scalar comparisons (eq, ne, gt, gte, lt, lte)" { test "TensorStatic | Scalar comparisons (eq, ne, gt, gte, lt, lte)" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const KiloMeter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1}); const KiloMeter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
@ -703,7 +703,7 @@ test "Scalar comparisons (eq, ne, gt, gte, lt, lte)" {
try std.testing.expect(m1000.lte(km2)); try std.testing.expect(m1000.lte(km2));
} }
test "Scalar Add" { test "TensorStatic | Scalar Add" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const KiloMeter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1}); const KiloMeter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
const KiloMeter_f = TensorStatic(f64, .{ .L = 1 }, .{ .L = .k }, &.{1}); const KiloMeter_f = TensorStatic(f64, .{ .L = 1 }, .{ .L = .k }, &.{1});
@ -726,7 +726,7 @@ test "Scalar Add" {
try std.testing.expectApproxEqAbs(2.01, added4.data[0], 0.000001); try std.testing.expectApproxEqAbs(2.01, added4.data[0], 0.000001);
} }
test "Scalar Sub" { test "TensorStatic | Scalar Sub" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const KiloMeter_f = TensorStatic(f64, .{ .L = 1 }, .{ .L = .k }, &.{1}); const KiloMeter_f = TensorStatic(f64, .{ .L = 1 }, .{ .L = .k }, &.{1});
@ -743,7 +743,7 @@ test "Scalar Sub" {
try std.testing.expectApproxEqAbs(2000, diff3.data[0], 1e-4); try std.testing.expectApproxEqAbs(2000, diff3.data[0], 1e-4);
} }
test "Scalar MulBy" { test "TensorStatic | Scalar MulBy" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const Second = TensorStatic(f32, .{ .T = 1 }, .{}, &.{1}); const Second = TensorStatic(f32, .{ .T = 1 }, .{}, &.{1});
@ -760,7 +760,7 @@ test "Scalar MulBy" {
try std.testing.expectEqual(2, @TypeOf(area).dims.get(.L)); try std.testing.expectEqual(2, @TypeOf(area).dims.get(.L));
} }
test "Scalar MulBy with scale" { test "TensorStatic | Scalar MulBy with scale" {
const KiloMeter = TensorStatic(f32, .{ .L = 1 }, .{ .L = .k }, &.{1}); const KiloMeter = TensorStatic(f32, .{ .L = 1 }, .{ .L = .k }, &.{1});
const KiloGram = TensorStatic(f32, .{ .M = 1 }, .{ .M = .k }, &.{1}); const KiloGram = TensorStatic(f32, .{ .M = 1 }, .{ .M = .k }, &.{1});
@ -771,7 +771,7 @@ test "Scalar MulBy with scale" {
try std.testing.expectEqual(1, @TypeOf(prod).dims.get(.M)); try std.testing.expectEqual(1, @TypeOf(prod).dims.get(.M));
} }
test "Scalar MulBy with type change" { test "TensorStatic | Scalar MulBy with type change" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
const Second = TensorStatic(f64, .{ .T = 1 }, .{}, &.{1}); const Second = TensorStatic(f64, .{ .T = 1 }, .{}, &.{1});
const KmSec = TensorStatic(i64, .{ .L = 1, .T = 1 }, .{ .L = .k }, &.{1}); const KmSec = TensorStatic(i64, .{ .L = 1, .T = 1 }, .{ .L = .k }, &.{1});
@ -784,7 +784,7 @@ test "Scalar MulBy with type change" {
try std.testing.expectApproxEqAbs(12.0, d.mul(t).to(KmSec_f).data[0], 0.0001); try std.testing.expectApproxEqAbs(12.0, d.mul(t).to(KmSec_f).data[0], 0.0001);
} }
test "Scalar MulBy small" { test "TensorStatic | Scalar MulBy small" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .n }, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .n }, &.{1});
const Second = TensorStatic(f32, .{ .T = 1 }, .{}, &.{1}); const Second = TensorStatic(f32, .{ .T = 1 }, .{}, &.{1});
const d = Meter.splat(3); const d = Meter.splat(3);
@ -792,7 +792,7 @@ test "Scalar MulBy small" {
try std.testing.expectEqual(12, d.mul(t).data[0]); try std.testing.expectEqual(12, d.mul(t).data[0]);
} }
test "Scalar MulBy dimensionless" { test "TensorStatic | Scalar MulBy dimensionless" {
const DimLess = TensorStatic(i128, .{}, .{}, &.{1}); const DimLess = TensorStatic(i128, .{}, .{}, &.{1});
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const d = Meter.splat(7); const d = Meter.splat(7);
@ -800,7 +800,7 @@ test "Scalar MulBy dimensionless" {
try std.testing.expectEqual(21, scaled.data[0]); try std.testing.expectEqual(21, scaled.data[0]);
} }
test "Scalar Sqrt" { test "TensorStatic | Scalar Sqrt" {
const MeterSquare = TensorStatic(i128, .{ .L = 2 }, .{}, &.{1}); const MeterSquare = TensorStatic(i128, .{ .L = 2 }, .{}, &.{1});
const MeterSquare_f = TensorStatic(f64, .{ .L = 2 }, .{}, &.{1}); const MeterSquare_f = TensorStatic(f64, .{ .L = 2 }, .{}, &.{1});
@ -818,7 +818,7 @@ test "Scalar Sqrt" {
try std.testing.expectApproxEqAbs(4.472135955, scaled2.data[0], 1e-4); try std.testing.expectApproxEqAbs(4.472135955, scaled2.data[0], 1e-4);
} }
test "Scalar Chained: velocity and acceleration" { test "TensorStatic | Scalar Chained: velocity and acceleration" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const Second = TensorStatic(f32, .{ .T = 1 }, .{}, &.{1}); const Second = TensorStatic(f32, .{ .T = 1 }, .{}, &.{1});
@ -832,7 +832,7 @@ test "Scalar Chained: velocity and acceleration" {
try std.testing.expectEqual(5, accel.data[0]); try std.testing.expectEqual(5, accel.data[0]);
} }
test "Scalar DivBy integer exact" { test "TensorStatic | Scalar DivBy integer exact" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const Second = TensorStatic(f32, .{ .T = 1 }, .{}, &.{1}); const Second = TensorStatic(f32, .{ .T = 1 }, .{}, &.{1});
@ -842,7 +842,7 @@ test "Scalar DivBy integer exact" {
try std.testing.expectEqual(30, vel.data[0]); try std.testing.expectEqual(30, vel.data[0]);
} }
test "Scalar Finer scales skip dim 0" { test "TensorStatic | Scalar Finer scales skip dim 0" {
const Dimless = TensorStatic(i128, .{}, .{}, &.{1}); const Dimless = TensorStatic(i128, .{}, .{}, &.{1});
const KiloMetre = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1}); const KiloMetre = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
@ -853,7 +853,7 @@ test "Scalar Finer scales skip dim 0" {
try std.testing.expectEqual(Scales.UnitScale.k, @TypeOf(vel).scales.get(.L)); try std.testing.expectEqual(Scales.UnitScale.k, @TypeOf(vel).scales.get(.L));
} }
test "Scalar Conversion chain: km -> m -> cm" { test "TensorStatic | Scalar Conversion chain: km -> m -> cm" {
const KiloMeter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1}); const KiloMeter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const CentiMeter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .c }, &.{1}); const CentiMeter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .c }, &.{1});
@ -865,7 +865,7 @@ test "Scalar Conversion chain: km -> m -> cm" {
try std.testing.expectEqual(1_500_000, cm.data[0]); try std.testing.expectEqual(1_500_000, cm.data[0]);
} }
test "Scalar Conversion: hours -> minutes -> seconds" { test "TensorStatic | Scalar Conversion: hours -> minutes -> seconds" {
const Hour = TensorStatic(i128, .{ .T = 1 }, .{ .T = .hour }, &.{1}); const Hour = TensorStatic(i128, .{ .T = 1 }, .{ .T = .hour }, &.{1});
const Minute = TensorStatic(i128, .{ .T = 1 }, .{ .T = .min }, &.{1}); const Minute = TensorStatic(i128, .{ .T = 1 }, .{ .T = .min }, &.{1});
const Second = TensorStatic(i128, .{ .T = 1 }, .{}, &.{1}); const Second = TensorStatic(i128, .{ .T = 1 }, .{}, &.{1});
@ -877,7 +877,7 @@ test "Scalar Conversion: hours -> minutes -> seconds" {
try std.testing.expectEqual(3600, sec.data[0]); try std.testing.expectEqual(3600, sec.data[0]);
} }
test "Scalar Format" { test "TensorStatic | Scalar Format" {
const MeterPerSecondSq = TensorStatic(f32, .{ .L = 1, .T = -2 }, .{ .T = .n }, &.{1}); const MeterPerSecondSq = TensorStatic(f32, .{ .L = 1, .T = -2 }, .{ .T = .n }, &.{1});
const Meter = TensorStatic(f32, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(f32, .{ .L = 1 }, .{}, &.{1});
@ -892,7 +892,7 @@ test "Scalar Format" {
try std.testing.expectEqualStrings("9.81m.ns⁻²", res); try std.testing.expectEqualStrings("9.81m.ns⁻²", res);
} }
test "Scalar Abs" { test "TensorStatic | Scalar Abs" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const MeterF = TensorStatic(f32, .{ .L = 1 }, .{}, &.{1}); const MeterF = TensorStatic(f32, .{ .L = 1 }, .{}, &.{1});
@ -900,21 +900,21 @@ test "Scalar Abs" {
try std.testing.expectEqual(42.5, MeterF.splat(-42.5).abs().data[0]); try std.testing.expectEqual(42.5, MeterF.splat(-42.5).abs().data[0]);
} }
test "Scalar Pow" { test "TensorStatic | Scalar Pow" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
const d = Meter.splat(4); const d = Meter.splat(4);
try std.testing.expectEqual(16, d.pow(2).data[0]); try std.testing.expectEqual(16, d.pow(2).data[0]);
try std.testing.expectEqual(64, d.pow(3).data[0]); try std.testing.expectEqual(64, d.pow(3).data[0]);
} }
test "Scalar add/sub bare number on dimensionless scalar" { test "TensorStatic | Scalar add/sub bare number on dimensionless scalar" {
const DimLess = TensorStatic(i128, .{}, .{}, &.{1}); const DimLess = TensorStatic(i128, .{}, .{}, &.{1});
const a = DimLess.splat(10); const a = DimLess.splat(10);
try std.testing.expectEqual(15, a.add(DimLess.splat(5)).data[0]); try std.testing.expectEqual(15, a.add(DimLess.splat(5)).data[0]);
try std.testing.expectEqual(7, a.sub(DimLess.splat(3)).data[0]); try std.testing.expectEqual(7, a.sub(DimLess.splat(3)).data[0]);
} }
test "Scalar Imperial length scales" { test "TensorStatic | Scalar Imperial length scales" {
const Foot = TensorStatic(f64, .{ .L = 1 }, .{ .L = .ft }, &.{1}); const Foot = TensorStatic(f64, .{ .L = 1 }, .{ .L = .ft }, &.{1});
const Meter = TensorStatic(f64, .{ .L = 1 }, .{}, &.{1}); const Meter = TensorStatic(f64, .{ .L = 1 }, .{}, &.{1});
const Inch = TensorStatic(f64, .{ .L = 1 }, .{ .L = .inch }, &.{1}); const Inch = TensorStatic(f64, .{ .L = 1 }, .{ .L = .inch }, &.{1});
@ -923,7 +923,7 @@ test "Scalar Imperial length scales" {
try std.testing.expectApproxEqAbs(1.0, Inch.splat(12.0).to(Foot).data[0], 1e-9); try std.testing.expectApproxEqAbs(1.0, Inch.splat(12.0).to(Foot).data[0], 1e-9);
} }
test "Scalar Imperial mass scales" { test "TensorStatic | Scalar Imperial mass scales" {
const Pound = TensorStatic(f64, .{ .M = 1 }, .{ .M = .lb }, &.{1}); const Pound = TensorStatic(f64, .{ .M = 1 }, .{ .M = .lb }, &.{1});
const Ounce = TensorStatic(f64, .{ .M = 1 }, .{ .M = .oz }, &.{1}); const Ounce = TensorStatic(f64, .{ .M = 1 }, .{ .M = .oz }, &.{1});
@ -933,14 +933,14 @@ test "Scalar Imperial mass scales" {
// Vector / Tensor tests // Vector / Tensor tests
test "Vector initiate" { test "TensorStatic | Vector initiate" {
const Meter4 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{4}); const Meter4 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{4});
const m = Meter4.splat(1); const m = Meter4.splat(1);
try std.testing.expect(m.data[0] == 1); try std.testing.expect(m.data[0] == 1);
try std.testing.expect(m.data[3] == 1); try std.testing.expect(m.data[3] == 1);
} }
test "Vector format" { test "TensorStatic | Vector format" {
const MeterPerSecondSq = TensorStatic(f32, .{ .L = 1, .T = -2 }, .{ .T = .n }, &.{3}); const MeterPerSecondSq = TensorStatic(f32, .{ .L = 1, .T = -2 }, .{ .T = .n }, &.{3});
const KgMeterPerSecond = TensorStatic(f32, .{ .M = 1, .L = 1, .T = -1 }, .{ .M = .k }, &.{3}); const KgMeterPerSecond = TensorStatic(f32, .{ .M = 1, .L = 1, .T = -1 }, .{ .M = .k }, &.{3});
@ -955,7 +955,7 @@ test "Vector format" {
try std.testing.expectEqualStrings("(43.00, 0.00, 11.00)m.kg.s⁻¹", res); try std.testing.expectEqualStrings("(43.00, 0.00, 11.00)m.kg.s⁻¹", res);
} }
test "Vector Vec3 Init and Basic Arithmetic" { test "TensorStatic | Vector Vec3 Init and Basic Arithmetic" {
const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3}); const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3});
const v_zero = Meter3.zero; const v_zero = Meter3.zero;
@ -987,7 +987,7 @@ test "Vector Vec3 Init and Basic Arithmetic" {
try std.testing.expectEqual(-30, neg.data[2]); try std.testing.expectEqual(-30, neg.data[2]);
} }
test "Vector Kinematics (scalar mul/div broadcast)" { test "TensorStatic | Vector Kinematics (scalar mul/div broadcast)" {
const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3}); const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3});
const Second1 = TensorStatic(i32, .{ .T = 1 }, .{}, &.{1}); const Second1 = TensorStatic(i32, .{ .T = 1 }, .{}, &.{1});
@ -1006,7 +1006,7 @@ test "Vector Kinematics (scalar mul/div broadcast)" {
try std.testing.expectEqual(0, @TypeOf(new_pos).dims.get(.T)); try std.testing.expectEqual(0, @TypeOf(new_pos).dims.get(.T));
} }
test "Vector Element-wise Math and Scaling" { test "TensorStatic | Vector Element-wise Math and Scaling" {
const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3}); const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3});
const v1 = Meter3{ .data = .{ 10, 20, 30 } }; const v1 = Meter3{ .data = .{ 10, 20, 30 } };
@ -1018,7 +1018,7 @@ test "Vector Element-wise Math and Scaling" {
try std.testing.expectEqual(0, @TypeOf(dv).dims.get(.L)); try std.testing.expectEqual(0, @TypeOf(dv).dims.get(.L));
} }
test "Vector Conversions" { test "TensorStatic | Vector Conversions" {
const KiloMeter3 = TensorStatic(i32, .{ .L = 1 }, .{ .L = .k }, &.{3}); const KiloMeter3 = TensorStatic(i32, .{ .L = 1 }, .{ .L = .k }, &.{3});
const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3}); const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3});
@ -1030,7 +1030,7 @@ test "Vector Conversions" {
try std.testing.expectEqual(UnitScale.none, @TypeOf(v_m).scales.get(.L)); try std.testing.expectEqual(UnitScale.none, @TypeOf(v_m).scales.get(.L));
} }
test "Vector Length" { test "TensorStatic | Vector Length" {
const MeterInt3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3}); const MeterInt3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3});
const MeterFloat3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3}); const MeterFloat3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3});
@ -1043,7 +1043,7 @@ test "Vector Length" {
try std.testing.expectApproxEqAbs(@as(f32, 5.0), v_float.length(), 1e-4); try std.testing.expectApproxEqAbs(@as(f32, 5.0), v_float.length(), 1e-4);
} }
test "Vector Comparisons" { test "TensorStatic | Vector Comparisons" {
const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3}); const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3});
const KiloMeter3 = TensorStatic(f32, .{ .L = 1 }, .{ .L = .k }, &.{3}); const KiloMeter3 = TensorStatic(f32, .{ .L = 1 }, .{ .L = .k }, &.{3});
@ -1068,7 +1068,7 @@ test "Vector Comparisons" {
try std.testing.expect(low_eq[0] and low_eq[1] and low_eq[2]); try std.testing.expect(low_eq[0] and low_eq[1] and low_eq[2]);
} }
test "Vector vs Scalar broadcast comparison" { test "TensorStatic | Vector vs Scalar broadcast comparison" {
const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3}); const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3});
const KiloMeter1 = TensorStatic(f32, .{ .L = 1 }, .{ .L = .k }, &.{1}); const KiloMeter1 = TensorStatic(f32, .{ .L = 1 }, .{ .L = .k }, &.{1});
@ -1086,7 +1086,7 @@ test "Vector vs Scalar broadcast comparison" {
try std.testing.expect(exact[1] == false); try std.testing.expect(exact[1] == false);
} }
test "Vector contract — dot product (rank-1 * rank-1)" { test "TensorStatic | Vector contract — dot product (rank-1 * rank-1)" {
const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3}); const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3});
const Newton3 = TensorStatic(f32, .{ .M = 1, .L = 1, .T = -2 }, .{}, &.{3}); const Newton3 = TensorStatic(f32, .{ .M = 1, .L = 1, .T = -2 }, .{}, &.{3});
@ -1100,7 +1100,7 @@ test "Vector contract — dot product (rank-1 * rank-1)" {
try std.testing.expectEqual(-2, @TypeOf(work).dims.get(.T)); try std.testing.expectEqual(-2, @TypeOf(work).dims.get(.T));
} }
test "Vector contract — matrix multiply (rank-2 * rank-2)" { test "TensorStatic | Vector contract — matrix multiply (rank-2 * rank-2)" {
const A = TensorStatic(f32, .{}, .{}, &.{ 2, 3 }); const A = TensorStatic(f32, .{}, .{}, &.{ 2, 3 });
const B = TensorStatic(f32, .{}, .{}, &.{ 3, 2 }); const B = TensorStatic(f32, .{}, .{}, &.{ 3, 2 });
@ -1114,7 +1114,7 @@ test "Vector contract — matrix multiply (rank-2 * rank-2)" {
try std.testing.expectEqual(154, c.data[TensorStatic(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 1, 1 })]); try std.testing.expectEqual(154, c.data[TensorStatic(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 1, 1 })]);
} }
test "Vector Abs, Pow, Sqrt and Product" { test "TensorStatic | Vector Abs, Pow, Sqrt and Product" {
const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3}); const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3});
const v1 = Meter3{ .data = .{ -2.0, 3.0, -4.0 } }; const v1 = Meter3{ .data = .{ -2.0, 3.0, -4.0 } };
@ -1137,7 +1137,7 @@ test "Vector Abs, Pow, Sqrt and Product" {
try std.testing.expectEqual(1, @TypeOf(sqrted).dims.get(.L)); try std.testing.expectEqual(1, @TypeOf(sqrted).dims.get(.L));
} }
test "Vector eq broadcast on dimensionless" { test "TensorStatic | Vector eq broadcast on dimensionless" {
const DimLess3 = TensorStatic(i32, .{}, .{}, &.{3}); const DimLess3 = TensorStatic(i32, .{}, .{}, &.{3});
const v = DimLess3{ .data = .{ 1, 2, 3 } }; const v = DimLess3{ .data = .{ 1, 2, 3 } };
@ -1147,7 +1147,7 @@ test "Vector eq broadcast on dimensionless" {
try std.testing.expectEqual(false, eq_res[2]); try std.testing.expectEqual(false, eq_res[2]);
} }
test "Tensor idx helper and matrix access" { test "TensorStatic | Tensor idx helper and matrix access" {
const Mat3x3 = TensorStatic(f32, .{}, .{}, &.{ 3, 3 }); const Mat3x3 = TensorStatic(f32, .{}, .{}, &.{ 3, 3 });
var m: Mat3x3 = Mat3x3.zero; var m: Mat3x3 = Mat3x3.zero;
m.data[Mat3x3.idx(.{ 0, 0 })] = 1.0; m.data[Mat3x3.idx(.{ 0, 0 })] = 1.0;
@ -1160,7 +1160,7 @@ test "Tensor idx helper and matrix access" {
try std.testing.expectEqual(0.0, m.data[1]); try std.testing.expectEqual(0.0, m.data[1]);
} }
test "Tensor strides_arr correctness" { test "TensorStatic | Tensor strides_arr correctness" {
const T1 = TensorStatic(f32, .{}, .{}, &.{3}); const T1 = TensorStatic(f32, .{}, .{}, &.{3});
const T2 = TensorStatic(f32, .{}, .{}, &.{ 3, 4 }); const T2 = TensorStatic(f32, .{}, .{}, &.{ 3, 4 });
const T3 = TensorStatic(f32, .{}, .{}, &.{ 2, 3, 4 }); const T3 = TensorStatic(f32, .{}, .{}, &.{ 2, 3, 4 });
@ -1173,7 +1173,7 @@ test "Tensor strides_arr correctness" {
try std.testing.expectEqual(1, T3.strides_arr[2]); try std.testing.expectEqual(1, T3.strides_arr[2]);
} }
test "Slice 1D basic" { test "TensorStatic | Slice 1D basic" {
const Vec = TensorStatic(i32, .{}, .{}, &.{5}); const Vec = TensorStatic(i32, .{}, .{}, &.{5});
var v = Vec{ .data = .{ 10, 20, 30, 40, 50 } }; var v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
const s = v.slice(.{.{ .start = 1, .end = 4 }}); const s = v.slice(.{.{ .start = 1, .end = 4 }});
@ -1183,7 +1183,7 @@ test "Slice 1D basic" {
try std.testing.expectEqual(40, s.data[2]); try std.testing.expectEqual(40, s.data[2]);
} }
test "Slice 1D full range" { test "TensorStatic | Slice 1D full range" {
const Vec = TensorStatic(f32, .{}, .{}, &.{4}); const Vec = TensorStatic(f32, .{}, .{}, &.{4});
const v = Vec{ .data = .{ 1.0, 2.0, 3.0, 4.0 } }; const v = Vec{ .data = .{ 1.0, 2.0, 3.0, 4.0 } };
const s = v.slice(.{.{ .start = 0, .end = 4 }}); const s = v.slice(.{.{ .start = 0, .end = 4 }});
@ -1191,7 +1191,7 @@ test "Slice 1D full range" {
inline for (0..4) |i| try std.testing.expectEqual(v.data[i], s.data[i]); inline for (0..4) |i| try std.testing.expectEqual(v.data[i], s.data[i]);
} }
test "Slice 1D single element" { test "TensorStatic | Slice 1D single element" {
const Vec = TensorStatic(i64, .{}, .{}, &.{6}); const Vec = TensorStatic(i64, .{}, .{}, &.{6});
const v = Vec{ .data = .{ 5, 10, 15, 20, 25, 30 } }; const v = Vec{ .data = .{ 5, 10, 15, 20, 25, 30 } };
const s = v.slice(.{.{ .start = 3, .end = 4 }}); const s = v.slice(.{.{ .start = 3, .end = 4 }});
@ -1199,7 +1199,7 @@ test "Slice 1D single element" {
try std.testing.expectEqual(20, s.data[0]); try std.testing.expectEqual(20, s.data[0]);
} }
test "Slice 1D preserves dims and scales" { test "TensorStatic | Slice 1D preserves dims and scales" {
const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{5}); const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{5});
const v = Meter{ .data = .{ 1, 2, 3, 4, 5 } }; const v = Meter{ .data = .{ 1, 2, 3, 4, 5 } };
const s = v.slice(.{.{ .start = 0, .end = 3 }}); const s = v.slice(.{.{ .start = 0, .end = 3 }});
@ -1208,7 +1208,7 @@ test "Slice 1D preserves dims and scales" {
try std.testing.expectEqual(Meter.scales.get(.L), S.scales.get(.L)); try std.testing.expectEqual(Meter.scales.get(.L), S.scales.get(.L));
} }
test "Slice 2D rows" { test "TensorStatic | Slice 2D rows" {
const Mat = TensorStatic(i32, .{}, .{}, &.{ 4, 3 }); const Mat = TensorStatic(i32, .{}, .{}, &.{ 4, 3 });
const m = Mat{ .data = .{ const m = Mat{ .data = .{
1, 2, 3, 1, 2, 3,
@ -1227,7 +1227,7 @@ test "Slice 2D rows" {
try std.testing.expectEqual(9, s.data[5]); try std.testing.expectEqual(9, s.data[5]);
} }
test "Slice 2D cols" { test "TensorStatic | Slice 2D cols" {
const Mat = TensorStatic(i32, .{}, .{}, &.{ 3, 4 }); const Mat = TensorStatic(i32, .{}, .{}, &.{ 3, 4 });
const m = Mat{ .data = .{ const m = Mat{ .data = .{
1, 2, 3, 4, 1, 2, 3, 4,
@ -1247,7 +1247,7 @@ test "Slice 2D cols" {
try std.testing.expectEqual(11, s.data[5]); try std.testing.expectEqual(11, s.data[5]);
} }
test "Slice 2D subblock" { test "TensorStatic | Slice 2D subblock" {
const Mat = TensorStatic(f64, .{}, .{}, &.{ 4, 4 }); const Mat = TensorStatic(f64, .{}, .{}, &.{ 4, 4 });
const m = Mat{ .data = .{ const m = Mat{ .data = .{
1, 2, 3, 4, 1, 2, 3, 4,
@ -1264,7 +1264,7 @@ test "Slice 2D subblock" {
try std.testing.expectApproxEqAbs(11.0, s.data[3], 1e-9); try std.testing.expectApproxEqAbs(11.0, s.data[3], 1e-9);
} }
test "Slice then add" { test "TensorStatic | Slice then add" {
const Meter = TensorStatic(i32, .{ .L = 1 }, .{}, &.{5}); const Meter = TensorStatic(i32, .{ .L = 1 }, .{}, &.{5});
const a = Meter{ .data = .{ 1, 2, 3, 4, 5 } }; const a = Meter{ .data = .{ 1, 2, 3, 4, 5 } };
const b = Meter{ .data = .{ 10, 20, 30, 40, 50 } }; const b = Meter{ .data = .{ 10, 20, 30, 40, 50 } };
@ -1276,7 +1276,7 @@ test "Slice then add" {
try std.testing.expectEqual(53, r.data[2]); // 3+50 try std.testing.expectEqual(53, r.data[2]); // 3+50
} }
test "Slice then scale convert" { test "TensorStatic | Slice then scale convert" {
const KiloMeter = TensorStatic(i64, .{ .L = 1 }, .{ .L = .k }, &.{4}); const KiloMeter = TensorStatic(i64, .{ .L = 1 }, .{ .L = .k }, &.{4});
const Meter = TensorStatic(i64, .{ .L = 1 }, .{}, &.{2}); const Meter = TensorStatic(i64, .{ .L = 1 }, .{}, &.{2});
const v = KiloMeter{ .data = .{ 1, 2, 3, 4 } }; const v = KiloMeter{ .data = .{ 1, 2, 3, 4 } };
@ -1286,7 +1286,7 @@ test "Slice then scale convert" {
try std.testing.expectEqual(3000, converted.data[1]); try std.testing.expectEqual(3000, converted.data[1]);
} }
test "Slice 1D negative start" { test "TensorStatic | Slice 1D negative start" {
const Vec = TensorStatic(i32, .{}, .{}, &.{5}); const Vec = TensorStatic(i32, .{}, .{}, &.{5});
const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } }; const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
const s = v.slice(.{.{ .start = -3, .end = 5 }}); // [2,5) 30,40,50 const s = v.slice(.{.{ .start = -3, .end = 5 }}); // [2,5) 30,40,50
@ -1296,7 +1296,7 @@ test "Slice 1D negative start" {
try std.testing.expectEqual(50, s.data[2]); try std.testing.expectEqual(50, s.data[2]);
} }
test "Slice 1D negative end" { test "TensorStatic | Slice 1D negative end" {
const Vec = TensorStatic(i32, .{}, .{}, &.{5}); const Vec = TensorStatic(i32, .{}, .{}, &.{5});
const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } }; const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
const s = v.slice(.{.{ .start = 1, .end = -1 }}); // [1,4) 20,30,40 const s = v.slice(.{.{ .start = 1, .end = -1 }}); // [1,4) 20,30,40
@ -1306,7 +1306,7 @@ test "Slice 1D negative end" {
try std.testing.expectEqual(40, s.data[2]); try std.testing.expectEqual(40, s.data[2]);
} }
test "Slice 1D both negative" { test "TensorStatic | Slice 1D both negative" {
const Vec = TensorStatic(i64, .{}, .{}, &.{6}); const Vec = TensorStatic(i64, .{}, .{}, &.{6});
const v = Vec{ .data = .{ 5, 10, 15, 20, 25, 30 } }; const v = Vec{ .data = .{ 5, 10, 15, 20, 25, 30 } };
const s = v.slice(.{.{ .start = -4, .end = -1 }}); // [2,5) 15,20,25 const s = v.slice(.{.{ .start = -4, .end = -1 }}); // [2,5) 15,20,25
@ -1316,7 +1316,7 @@ test "Slice 1D both negative" {
try std.testing.expectEqual(25, s.data[2]); try std.testing.expectEqual(25, s.data[2]);
} }
test "Slice 1D null start" { test "TensorStatic | Slice 1D null start" {
const Vec = TensorStatic(i32, .{}, .{}, &.{5}); const Vec = TensorStatic(i32, .{}, .{}, &.{5});
const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } }; const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
const s = v.slice(.{.{ .end = -2 }}); // [:-2] 10,20,30 const s = v.slice(.{.{ .end = -2 }}); // [:-2] 10,20,30
@ -1326,7 +1326,7 @@ test "Slice 1D null start" {
try std.testing.expectEqual(30, s.data[2]); try std.testing.expectEqual(30, s.data[2]);
} }
test "Slice 1D null end" { test "TensorStatic | Slice 1D null end" {
const Vec = TensorStatic(i32, .{}, .{}, &.{5}); const Vec = TensorStatic(i32, .{}, .{}, &.{5});
const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } }; const v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
const s = v.slice(.{.{ .start = -3 }}); // [-3:] 30,40,50 const s = v.slice(.{.{ .start = -3 }}); // [-3:] 30,40,50
@ -1336,7 +1336,7 @@ test "Slice 1D null end" {
try std.testing.expectEqual(50, s.data[2]); try std.testing.expectEqual(50, s.data[2]);
} }
test "Slice 2D negative & null indices" { test "TensorStatic | Slice 2D negative & null indices" {
const Mat = TensorStatic(i32, .{}, .{}, &.{ 4, 4 }); const Mat = TensorStatic(i32, .{}, .{}, &.{ 4, 4 });
const m = Mat{ .data = .{ const m = Mat{ .data = .{
1, 2, 3, 4, 1, 2, 3, 4,