TensorAlloc add and to compilable but still error for basic add test
This commit is contained in:
parent
f702c1e09a
commit
e6d0f62929
@ -3,6 +3,7 @@ const Scales = @import("Scales.zig");
|
||||
const UnitScale = Scales.UnitScale;
|
||||
const Dimensions = @import("Dimensions.zig");
|
||||
const Dimension = Dimensions.Dimension;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const sh = @import("shared.zig");
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@ -50,11 +51,11 @@ pub fn TensorAlloc(
|
||||
|
||||
// Specific to Alloc
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !@This() {
|
||||
pub fn init(allocator: Allocator) !@This() {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -72,7 +73,7 @@ pub fn TensorAlloc(
|
||||
}
|
||||
|
||||
/// 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);
|
||||
new.data.* = @splat(v);
|
||||
return new;
|
||||
@ -88,7 +89,7 @@ pub fn TensorAlloc(
|
||||
|
||||
/// Element-wise add. Dimensions must match; scales resolve to finer.
|
||||
/// 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,
|
||||
dims.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))
|
||||
@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 l: Vec = self.to(TargetType).data;
|
||||
const r: Vec = rhs.to(TargetType).data;
|
||||
return .{ .data = if (comptime sh.isInt(T)) l +| r else l + r };
|
||||
const l: TargetType = try self.to(area.allocator(), TargetType);
|
||||
const r: TargetType = try rhs.to(area.allocator(), TargetType);
|
||||
|
||||
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.
|
||||
@ -226,8 +233,9 @@ pub fn TensorAlloc(
|
||||
/// • Scale ratio is computed fully at comptime; only a SIMD multiply at runtime.
|
||||
pub inline fn to(
|
||||
self: *const Self,
|
||||
alloc: Allocator,
|
||||
comptime Dest: type,
|
||||
) Dest {
|
||||
) !Dest {
|
||||
if (comptime Self == Dest) return self.*;
|
||||
|
||||
// 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))
|
||||
@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 DestT = Dest.ValueType;
|
||||
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 Dest_info = @typeInfo(DestT);
|
||||
|
||||
return .{
|
||||
.data = if (comptime T_info == .int and Dest_info == .int)
|
||||
@as(DestVec, @intCast(vec.data))
|
||||
const vec = try TensorAlloc(Dest.ValueType, dims.argsOpt(), scales.argsOpt(), Dest.shape).splat(
|
||||
alloc,
|
||||
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)
|
||||
@as(DestVec, @floatCast(vec.data))
|
||||
@as(DestT, @floatCast(self.data[0]))
|
||||
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)
|
||||
@as(DestVec, @intFromFloat(vec.data))
|
||||
@as(DestT, @intFromFloat(self.data[0]))
|
||||
else
|
||||
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 @typeInfo(T) == .float)
|
||||
return .{ .data = vec.data * @as(DestVec, @splat(@as(T, @floatCast(ratio)))) };
|
||||
if (comptime @typeInfo(T) == .float) {
|
||||
new.data.* = vec.data.* * @as(DestVec, @splat(@as(T, @floatCast(ratio))));
|
||||
return new;
|
||||
}
|
||||
|
||||
if (comptime ratio >= 1.0) {
|
||||
const mult: T = comptime @intFromFloat(@round(ratio));
|
||||
return .{ .data = vec.data *| @as(Vec, @splat(mult)) };
|
||||
new.data.* = vec.data.* *| @as(Vec, @splat(mult));
|
||||
} else {
|
||||
const div_val: T = comptime @intFromFloat(@round(1.0 / ratio));
|
||||
const half: T = comptime @divTrunc(div_val, 2);
|
||||
|
||||
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 {
|
||||
// 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)));
|
||||
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
|
||||
const FVec = @Vector(total, f64);
|
||||
const float_vec: FVec = switch (comptime @typeInfo(T)) {
|
||||
.float => @floatCast(vec.data),
|
||||
.int => @floatFromInt(vec.data),
|
||||
.float => @floatCast(vec.data.*),
|
||||
.int => @floatFromInt(vec.data.*),
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
const scaled = float_vec * @as(FVec, @splat(ratio));
|
||||
|
||||
return switch (comptime @typeInfo(DestT)) {
|
||||
.float => .{ .data = @floatCast(scaled) },
|
||||
.int => .{ .data = @intFromFloat(@round(scaled)) },
|
||||
new.data.* = switch (comptime @typeInfo(DestT)) {
|
||||
.float => @floatCast(scaled),
|
||||
.int => @intFromFloat(@round(scaled)),
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
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.
|
||||
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);
|
||||
if (comptime !isTensor(RhsType))
|
||||
@compileError("rhs can only be a Tensor ");
|
||||
@ -622,28 +641,32 @@ pub fn TensorAlloc(
|
||||
|
||||
// ─── Scalar tests ─────────────────────────────────────────────────────────
|
||||
|
||||
test "Scalar initiat" {
|
||||
const alloc = std.testing.allocator;
|
||||
test "TensorAlloc | Scalar initiat" {
|
||||
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 Second = TensorAlloc(f32, .{ .T = 1 }, .{ .T = .n }, &.{1});
|
||||
|
||||
const distance = try Meter.splat(alloc, 10);
|
||||
defer distance.deinit(alloc);
|
||||
const time = try Second.splat(alloc, 2);
|
||||
defer time.deinit(alloc);
|
||||
|
||||
try std.testing.expectEqual(10, distance.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 KiloMeter = TensorAlloc(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
|
||||
//
|
||||
// const m1000 = Meter.splat(1000);
|
||||
// const km1 = KiloMeter.splat(1);
|
||||
// const km2 = KiloMeter.splat(2);
|
||||
// const m1000 = try Meter.splat(alloc, 1000);
|
||||
// const km1 = try KiloMeter.splat(alloc, 1);
|
||||
// const km2 = try KiloMeter.splat(alloc, 2);
|
||||
//
|
||||
// try std.testing.expect(m1000.eq(km1));
|
||||
// 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(m1000.lte(km2));
|
||||
// }
|
||||
//
|
||||
// test "Scalar Add" {
|
||||
// const Meter = TensorAlloc(i128, .{ .L = 1 }, .{}, &.{1});
|
||||
// const KiloMeter = TensorAlloc(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
|
||||
// const KiloMeter_f = TensorAlloc(f64, .{ .L = 1 }, .{ .L = .k }, &.{1});
|
||||
//
|
||||
// const distance = Meter.splat(10);
|
||||
// const distance2 = Meter.splat(20);
|
||||
// const added = distance.add(distance2);
|
||||
// try std.testing.expectEqual(30, added.data[0]);
|
||||
// try std.testing.expectEqual(1, @TypeOf(added).dims.get(.L));
|
||||
//
|
||||
// const distance3 = KiloMeter.splat(2);
|
||||
// const added2 = distance.add(distance3);
|
||||
// try std.testing.expectEqual(2010, added2.data[0]);
|
||||
//
|
||||
// const added3 = distance3.add(distance).to(KiloMeter);
|
||||
// try std.testing.expectEqual(2, added3.data[0]);
|
||||
//
|
||||
// const distance4 = KiloMeter_f.splat(2);
|
||||
// const added4 = distance4.add(distance).to(KiloMeter_f);
|
||||
// try std.testing.expectApproxEqAbs(2.01, added4.data[0], 0.000001);
|
||||
// }
|
||||
//
|
||||
|
||||
test "TensorAlloc | Scalar Add" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Meter = TensorAlloc(i128, .{ .L = 1 }, .{}, &.{1});
|
||||
const KiloMeter = TensorAlloc(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
|
||||
const KiloMeter_f = TensorAlloc(f64, .{ .L = 1 }, .{ .L = .k }, &.{1});
|
||||
|
||||
const distance = try Meter.splat(alloc, 10);
|
||||
const distance2 = try Meter.splat(alloc, 20);
|
||||
const added = try distance.add(alloc, distance2);
|
||||
try std.testing.expectEqual(30, added.data[0]);
|
||||
try std.testing.expectEqual(1, @TypeOf(added).dims.get(.L));
|
||||
|
||||
const distance3 = try KiloMeter.splat(alloc, 2);
|
||||
const added2 = try distance.add(alloc, distance3);
|
||||
try std.testing.expectEqual(2010, added2.data[0]);
|
||||
|
||||
const added3 = try (try distance3.add(alloc, distance)).to(alloc, KiloMeter);
|
||||
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" {
|
||||
// const Meter = TensorAlloc(i128, .{ .L = 1 }, .{}, &.{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(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]);
|
||||
// }
|
||||
|
||||
@ -669,7 +669,7 @@ pub fn TensorStatic(
|
||||
|
||||
// ─── Scalar tests ─────────────────────────────────────────────────────────
|
||||
|
||||
test "Scalar initiat" {
|
||||
test "TensorStatic | Scalar initiat" {
|
||||
const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = @enumFromInt(-3) }, &.{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]);
|
||||
}
|
||||
|
||||
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 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));
|
||||
}
|
||||
|
||||
test "Scalar Add" {
|
||||
test "TensorStatic | Scalar Add" {
|
||||
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
|
||||
const KiloMeter = TensorStatic(i128, .{ .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);
|
||||
}
|
||||
|
||||
test "Scalar Sub" {
|
||||
test "TensorStatic | Scalar Sub" {
|
||||
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{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);
|
||||
}
|
||||
|
||||
test "Scalar MulBy" {
|
||||
test "TensorStatic | Scalar MulBy" {
|
||||
const Meter = TensorStatic(i128, .{ .L = 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));
|
||||
}
|
||||
|
||||
test "Scalar MulBy with scale" {
|
||||
test "TensorStatic | Scalar MulBy with scale" {
|
||||
const KiloMeter = TensorStatic(f32, .{ .L = 1 }, .{ .L = .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));
|
||||
}
|
||||
|
||||
test "Scalar MulBy with type change" {
|
||||
test "TensorStatic | Scalar MulBy with type change" {
|
||||
const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
|
||||
const Second = TensorStatic(f64, .{ .T = 1 }, .{}, &.{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);
|
||||
}
|
||||
|
||||
test "Scalar MulBy small" {
|
||||
test "TensorStatic | Scalar MulBy small" {
|
||||
const Meter = TensorStatic(i128, .{ .L = 1 }, .{ .L = .n }, &.{1});
|
||||
const Second = TensorStatic(f32, .{ .T = 1 }, .{}, &.{1});
|
||||
const d = Meter.splat(3);
|
||||
@ -792,7 +792,7 @@ test "Scalar MulBy small" {
|
||||
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 Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
|
||||
const d = Meter.splat(7);
|
||||
@ -800,7 +800,7 @@ test "Scalar MulBy dimensionless" {
|
||||
try std.testing.expectEqual(21, scaled.data[0]);
|
||||
}
|
||||
|
||||
test "Scalar Sqrt" {
|
||||
test "TensorStatic | Scalar Sqrt" {
|
||||
const MeterSquare = TensorStatic(i128, .{ .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);
|
||||
}
|
||||
|
||||
test "Scalar Chained: velocity and acceleration" {
|
||||
test "TensorStatic | Scalar Chained: velocity and acceleration" {
|
||||
const Meter = TensorStatic(i128, .{ .L = 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]);
|
||||
}
|
||||
|
||||
test "Scalar DivBy integer exact" {
|
||||
test "TensorStatic | Scalar DivBy integer exact" {
|
||||
const Meter = TensorStatic(i128, .{ .L = 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]);
|
||||
}
|
||||
|
||||
test "Scalar Finer scales skip dim 0" {
|
||||
test "TensorStatic | Scalar Finer scales skip dim 0" {
|
||||
const Dimless = TensorStatic(i128, .{}, .{}, &.{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));
|
||||
}
|
||||
|
||||
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 Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{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]);
|
||||
}
|
||||
|
||||
test "Scalar Conversion: hours -> minutes -> seconds" {
|
||||
test "TensorStatic | Scalar Conversion: hours -> minutes -> seconds" {
|
||||
const Hour = TensorStatic(i128, .{ .T = 1 }, .{ .T = .hour }, &.{1});
|
||||
const Minute = TensorStatic(i128, .{ .T = 1 }, .{ .T = .min }, &.{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]);
|
||||
}
|
||||
|
||||
test "Scalar Format" {
|
||||
test "TensorStatic | Scalar Format" {
|
||||
const MeterPerSecondSq = TensorStatic(f32, .{ .L = 1, .T = -2 }, .{ .T = .n }, &.{1});
|
||||
const Meter = TensorStatic(f32, .{ .L = 1 }, .{}, &.{1});
|
||||
|
||||
@ -892,7 +892,7 @@ test "Scalar Format" {
|
||||
try std.testing.expectEqualStrings("9.81m.ns⁻²", res);
|
||||
}
|
||||
|
||||
test "Scalar Abs" {
|
||||
test "TensorStatic | Scalar Abs" {
|
||||
const Meter = TensorStatic(i128, .{ .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]);
|
||||
}
|
||||
|
||||
test "Scalar Pow" {
|
||||
test "TensorStatic | Scalar Pow" {
|
||||
const Meter = TensorStatic(i128, .{ .L = 1 }, .{}, &.{1});
|
||||
const d = Meter.splat(4);
|
||||
try std.testing.expectEqual(16, d.pow(2).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 a = DimLess.splat(10);
|
||||
try std.testing.expectEqual(15, a.add(DimLess.splat(5)).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 Meter = TensorStatic(f64, .{ .L = 1 }, .{}, &.{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);
|
||||
}
|
||||
|
||||
test "Scalar Imperial mass scales" {
|
||||
test "TensorStatic | Scalar Imperial mass scales" {
|
||||
const Pound = TensorStatic(f64, .{ .M = 1 }, .{ .M = .lb }, &.{1});
|
||||
const Ounce = TensorStatic(f64, .{ .M = 1 }, .{ .M = .oz }, &.{1});
|
||||
|
||||
@ -933,14 +933,14 @@ test "Scalar Imperial mass scales" {
|
||||
|
||||
// ─── Vector / Tensor tests ────────────────────────────────────────────────
|
||||
|
||||
test "Vector initiate" {
|
||||
test "TensorStatic | Vector initiate" {
|
||||
const Meter4 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{4});
|
||||
const m = Meter4.splat(1);
|
||||
try std.testing.expect(m.data[0] == 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 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);
|
||||
}
|
||||
|
||||
test "Vector Vec3 Init and Basic Arithmetic" {
|
||||
test "TensorStatic | Vector Vec3 Init and Basic Arithmetic" {
|
||||
const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
|
||||
const v_zero = Meter3.zero;
|
||||
@ -987,7 +987,7 @@ test "Vector Vec3 Init and Basic Arithmetic" {
|
||||
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 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));
|
||||
}
|
||||
|
||||
test "Vector Element-wise Math and Scaling" {
|
||||
test "TensorStatic | Vector Element-wise Math and Scaling" {
|
||||
const Meter3 = TensorStatic(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
test "Vector Conversions" {
|
||||
test "TensorStatic | Vector Conversions" {
|
||||
const KiloMeter3 = TensorStatic(i32, .{ .L = 1 }, .{ .L = .k }, &.{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));
|
||||
}
|
||||
|
||||
test "Vector Length" {
|
||||
test "TensorStatic | Vector Length" {
|
||||
const MeterInt3 = TensorStatic(i32, .{ .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);
|
||||
}
|
||||
|
||||
test "Vector Comparisons" {
|
||||
test "TensorStatic | Vector Comparisons" {
|
||||
const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{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]);
|
||||
}
|
||||
|
||||
test "Vector vs Scalar broadcast comparison" {
|
||||
test "TensorStatic | Vector vs Scalar broadcast comparison" {
|
||||
const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
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);
|
||||
}
|
||||
|
||||
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 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));
|
||||
}
|
||||
|
||||
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 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 })]);
|
||||
}
|
||||
|
||||
test "Vector Abs, Pow, Sqrt and Product" {
|
||||
test "TensorStatic | Vector Abs, Pow, Sqrt and Product" {
|
||||
const Meter3 = TensorStatic(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
test "Vector eq broadcast on dimensionless" {
|
||||
test "TensorStatic | Vector eq broadcast on dimensionless" {
|
||||
const DimLess3 = TensorStatic(i32, .{}, .{}, &.{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]);
|
||||
}
|
||||
|
||||
test "Tensor idx helper and matrix access" {
|
||||
test "TensorStatic | Tensor idx helper and matrix access" {
|
||||
const Mat3x3 = TensorStatic(f32, .{}, .{}, &.{ 3, 3 });
|
||||
var m: Mat3x3 = Mat3x3.zero;
|
||||
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]);
|
||||
}
|
||||
|
||||
test "Tensor strides_arr correctness" {
|
||||
test "TensorStatic | Tensor strides_arr correctness" {
|
||||
const T1 = TensorStatic(f32, .{}, .{}, &.{3});
|
||||
const T2 = TensorStatic(f32, .{}, .{}, &.{ 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]);
|
||||
}
|
||||
|
||||
test "Slice 1D basic" {
|
||||
test "TensorStatic | Slice 1D basic" {
|
||||
const Vec = TensorStatic(i32, .{}, .{}, &.{5});
|
||||
var v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
|
||||
const s = v.slice(.{.{ .start = 1, .end = 4 }});
|
||||
@ -1183,7 +1183,7 @@ test "Slice 1D basic" {
|
||||
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 v = Vec{ .data = .{ 1.0, 2.0, 3.0, 4.0 } };
|
||||
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]);
|
||||
}
|
||||
|
||||
test "Slice 1D single element" {
|
||||
test "TensorStatic | Slice 1D single element" {
|
||||
const Vec = TensorStatic(i64, .{}, .{}, &.{6});
|
||||
const v = Vec{ .data = .{ 5, 10, 15, 20, 25, 30 } };
|
||||
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]);
|
||||
}
|
||||
|
||||
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 v = Meter{ .data = .{ 1, 2, 3, 4, 5 } };
|
||||
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));
|
||||
}
|
||||
|
||||
test "Slice 2D rows" {
|
||||
test "TensorStatic | Slice 2D rows" {
|
||||
const Mat = TensorStatic(i32, .{}, .{}, &.{ 4, 3 });
|
||||
const m = Mat{ .data = .{
|
||||
1, 2, 3,
|
||||
@ -1227,7 +1227,7 @@ test "Slice 2D rows" {
|
||||
try std.testing.expectEqual(9, s.data[5]);
|
||||
}
|
||||
|
||||
test "Slice 2D cols" {
|
||||
test "TensorStatic | Slice 2D cols" {
|
||||
const Mat = TensorStatic(i32, .{}, .{}, &.{ 3, 4 });
|
||||
const m = Mat{ .data = .{
|
||||
1, 2, 3, 4,
|
||||
@ -1247,7 +1247,7 @@ test "Slice 2D cols" {
|
||||
try std.testing.expectEqual(11, s.data[5]);
|
||||
}
|
||||
|
||||
test "Slice 2D subblock" {
|
||||
test "TensorStatic | Slice 2D subblock" {
|
||||
const Mat = TensorStatic(f64, .{}, .{}, &.{ 4, 4 });
|
||||
const m = Mat{ .data = .{
|
||||
1, 2, 3, 4,
|
||||
@ -1264,7 +1264,7 @@ test "Slice 2D subblock" {
|
||||
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 a = Meter{ .data = .{ 1, 2, 3, 4, 5 } };
|
||||
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
|
||||
}
|
||||
|
||||
test "Slice then scale convert" {
|
||||
test "TensorStatic | Slice then scale convert" {
|
||||
const KiloMeter = TensorStatic(i64, .{ .L = 1 }, .{ .L = .k }, &.{4});
|
||||
const Meter = TensorStatic(i64, .{ .L = 1 }, .{}, &.{2});
|
||||
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]);
|
||||
}
|
||||
|
||||
test "Slice 1D negative start" {
|
||||
test "TensorStatic | 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
|
||||
@ -1296,7 +1296,7 @@ test "Slice 1D negative start" {
|
||||
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 v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
|
||||
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]);
|
||||
}
|
||||
|
||||
test "Slice 1D both negative" {
|
||||
test "TensorStatic | 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
|
||||
@ -1316,7 +1316,7 @@ test "Slice 1D both negative" {
|
||||
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 v = Vec{ .data = .{ 10, 20, 30, 40, 50 } };
|
||||
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]);
|
||||
}
|
||||
|
||||
test "Slice 1D null end" {
|
||||
test "TensorStatic | 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
|
||||
@ -1336,7 +1336,7 @@ test "Slice 1D null end" {
|
||||
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 m = Mat{ .data = .{
|
||||
1, 2, 3, 4,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user