Added all operation and test for TensorAlloc, missing slice
This commit is contained in:
parent
5ac9968021
commit
ff21f0ac8b
@ -65,6 +65,19 @@ pub fn Tensor(
|
||||
return new;
|
||||
}
|
||||
|
||||
/// Convert N-D coords (row-major) to flat index — fully comptime.
|
||||
/// Usage: Tensor.idx(.{row, col})
|
||||
pub inline fn idx(comptime coords: [rank]usize) usize {
|
||||
comptime {
|
||||
var flat: usize = 0;
|
||||
for (0..rank) |i| {
|
||||
if (coords[i] >= shape[i]) @compileError("idx: Coordinate out of bounds");
|
||||
flat += coords[i] * strides_arr[i];
|
||||
}
|
||||
return flat;
|
||||
}
|
||||
}
|
||||
|
||||
/// Element-wise add. Dimensions must match; scales resolve to finer.
|
||||
/// RHS must have the same shape as self, or total == 1 (broadcast).
|
||||
pub fn add(self: *const Self, alloc: Allocator, rhs: anytype) !Tensor(
|
||||
@ -470,6 +483,197 @@ pub fn Tensor(
|
||||
return !(try self.eqAll(alloc, other));
|
||||
}
|
||||
|
||||
pub fn contract(
|
||||
self: *const Self,
|
||||
alloc: Allocator,
|
||||
rhs: anytype,
|
||||
comptime axis_a: usize,
|
||||
comptime axis_b: usize,
|
||||
) !blk: {
|
||||
const RhsType = @TypeOf(rhs);
|
||||
if (!sh.isTensor(RhsType))
|
||||
@compileError("rhs can only be a Tensor ");
|
||||
if (axis_a >= rank) @compileError("contract: axis_a out of bounds");
|
||||
if (axis_b >= RhsType.rank) @compileError("contract: axis_b out of bounds");
|
||||
if (shape[axis_a] != RhsType.shape[axis_b]) @compileError("contract: shape mismatch at contraction axes");
|
||||
|
||||
const sa = sh.shapeRemoveAxis(shape, axis_a);
|
||||
const sb = sh.shapeRemoveAxis(RhsType.shape, axis_b);
|
||||
const rs_raw = sh.shapeCat(&sa, &sb);
|
||||
const rs: []const comptime_int = if (rs_raw.len == 0) &.{1} else &rs_raw;
|
||||
break :blk Tensor(
|
||||
T,
|
||||
dims.add(RhsType.dims).argsOpt(),
|
||||
sh.finerScales(Self, RhsType).argsOpt(),
|
||||
rs,
|
||||
);
|
||||
} {
|
||||
const RhsType = @TypeOf(rhs);
|
||||
const k: usize = comptime shape[axis_a]; // contraction dimension
|
||||
|
||||
const sa = comptime sh.shapeRemoveAxis(shape, axis_a);
|
||||
const sb = comptime sh.shapeRemoveAxis(RhsType.shape, axis_b);
|
||||
const rs_raw = comptime sh.shapeCat(&sa, &sb);
|
||||
const rs: []const comptime_int = comptime if (rs_raw.len == 0) &.{1} else &rs_raw;
|
||||
|
||||
const ResultType = Tensor(
|
||||
T,
|
||||
dims.add(RhsType.dims).argsOpt(),
|
||||
sh.finerScales(Self, RhsType).argsOpt(),
|
||||
rs,
|
||||
);
|
||||
|
||||
const SelfNorm = Tensor(T, dims.argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape);
|
||||
const OtherNorm = Tensor(T, RhsType.dims.argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), RhsType.shape);
|
||||
|
||||
// Normalize both tensors to matching, finer scales safely with allocations
|
||||
const l = try self.to(alloc, SelfNorm);
|
||||
defer l.deinit(alloc);
|
||||
const r = try rhs.to(alloc, OtherNorm);
|
||||
defer r.deinit(alloc);
|
||||
|
||||
const ResVec = @Vector(ResultType.total, T);
|
||||
|
||||
// FAST PATH: Dot Product
|
||||
if (comptime rank == 1 and RhsType.rank == 1 and axis_a == 0 and axis_b == 0) {
|
||||
const result_vec: ResVec = if (comptime !sh.isInt(T)) blk: {
|
||||
break :blk @splat(@reduce(.Add, l.data.* * r.data.*));
|
||||
} else blk: {
|
||||
const mul_arr: [total]T = l.data.* *| r.data.*;
|
||||
var acc: T = 0;
|
||||
for (mul_arr) |val| acc +|= val;
|
||||
break :blk @splat(acc);
|
||||
};
|
||||
|
||||
const vec_ptr = try alloc.create(ResVec);
|
||||
vec_ptr.* = result_vec;
|
||||
return ResultType{ .data = vec_ptr };
|
||||
}
|
||||
|
||||
// --- ZERO-COST COERCION TO ARRAYS FOR RUNTIME INDEXING ---
|
||||
const a_arr: [total]T = l.data.*;
|
||||
const b_arr: [RhsType.total]T = r.data.*;
|
||||
|
||||
// FAST PATH: 2D Matrix Multiplication
|
||||
if (comptime rank == 2 and RhsType.rank == 2 and axis_a == 1 and axis_b == 0) {
|
||||
const rows = shape[0];
|
||||
const cols = RhsType.shape[1];
|
||||
const inner = shape[1];
|
||||
|
||||
var res_arr: [ResultType.total]T = undefined;
|
||||
|
||||
for (0..rows) |i| {
|
||||
for (0..cols) |j| {
|
||||
var acc: T = 0;
|
||||
for (0..inner) |id| {
|
||||
const a_flat = i * _strides[0] + id * _strides[1];
|
||||
const b_flat = id * RhsType.strides_arr[0] + j * RhsType.strides_arr[1];
|
||||
|
||||
if (comptime sh.isInt(T)) acc +|= a_arr[a_flat] *| b_arr[b_flat] else acc += a_arr[a_flat] * b_arr[b_flat];
|
||||
}
|
||||
res_arr[i * cols + j] = acc;
|
||||
}
|
||||
}
|
||||
|
||||
const vec_ptr = try alloc.create(ResVec);
|
||||
vec_ptr.* = res_arr;
|
||||
return ResultType{ .data = vec_ptr };
|
||||
}
|
||||
|
||||
// FALLBACK PATH
|
||||
const rs_raw_strides = comptime sh.shapeStrides(&rs_raw);
|
||||
var result_arr: [ResultType.total]T = undefined;
|
||||
|
||||
for (0..ResultType.total) |res_flat| {
|
||||
const res_coords = sh.decodeFlatCoords(res_flat, rs_raw.len, rs_raw_strides);
|
||||
|
||||
var a_free: [sa.len]usize = undefined;
|
||||
for (0..sa.len) |i| a_free[i] = res_coords[i];
|
||||
var b_free: [sb.len]usize = undefined;
|
||||
for (0..sb.len) |i| b_free[i] = res_coords[sa.len + i];
|
||||
|
||||
var acc: T = 0;
|
||||
for (0..k) |ki| {
|
||||
const a_coords = sh.insertAxis(rank, axis_a, ki, &a_free);
|
||||
const b_coords = sh.insertAxis(RhsType.rank, axis_b, ki, &b_free);
|
||||
const a_flat = sh.encodeFlatCoords(&a_coords, rank, _strides);
|
||||
const b_flat = sh.encodeFlatCoords(&b_coords, RhsType.rank, RhsType.strides_arr);
|
||||
|
||||
if (comptime sh.isInt(T)) acc +|= a_arr[a_flat] *| b_arr[b_flat] else acc += a_arr[a_flat] * b_arr[b_flat];
|
||||
}
|
||||
result_arr[res_flat] = acc;
|
||||
}
|
||||
|
||||
const vec_ptr = try alloc.create(ResVec);
|
||||
vec_ptr.* = result_arr;
|
||||
return ResultType{ .data = vec_ptr };
|
||||
}
|
||||
|
||||
/// 3D Cross Product. Only defined for Rank-1 tensors of length 3.
|
||||
/// Result dimensions are the sum of input dimensions.
|
||||
pub fn cross(self: *const Self, alloc: Allocator, rhs: anytype) !Tensor(
|
||||
T,
|
||||
dims.add(@TypeOf(rhs).dims).argsOpt(),
|
||||
sh.finerScales(Self, @TypeOf(rhs)).argsOpt(),
|
||||
&.{3},
|
||||
) {
|
||||
const RhsType = @TypeOf(rhs);
|
||||
|
||||
if (!sh.isTensor(RhsType))
|
||||
@compileError("rhs can only be a Tensor ");
|
||||
if (comptime rank != 1 or shape[0] != 3 or RhsType.rank != 1 or RhsType.shape[0] != 3)
|
||||
@compileError("cross product is only defined for 3D vectors (rank-1, length 3)");
|
||||
|
||||
// Bring both to the same scale (e.g., mm vs m)
|
||||
const p = try self.resolveScalePair(alloc, rhs);
|
||||
defer p.deinit(alloc);
|
||||
const l = p.l.data;
|
||||
const r = p.r.data;
|
||||
|
||||
var res: [3]T = undefined;
|
||||
if (comptime sh.isInt(T)) {
|
||||
res[0] = (l[1] *| r[2]) -| (l[2] *| r[1]);
|
||||
res[1] = (l[2] *| r[0]) -| (l[0] *| r[2]);
|
||||
res[2] = (l[0] *| r[1]) -| (l[1] *| r[0]);
|
||||
} else {
|
||||
res[0] = (l[1] * r[2]) - (l[2] * r[1]);
|
||||
res[1] = (l[2] * r[0]) - (l[0] * r[2]);
|
||||
res[2] = (l[0] * r[1]) - (l[1] * r[0]);
|
||||
}
|
||||
|
||||
return try .load(alloc, res);
|
||||
}
|
||||
|
||||
/// Sum of squared elements. Cheaper than length(); use for ordering.
|
||||
pub fn lengthSqr(self: *const Self) T {
|
||||
return @reduce(.Add, self.data.* * self.data.*);
|
||||
}
|
||||
|
||||
/// Euclidean length (L2 norm).
|
||||
pub fn length(self: *const Self) T {
|
||||
const sq = self.lengthSqr();
|
||||
if (comptime @typeInfo(T) == .int) {
|
||||
const UnsignedT = @Int(.unsigned, @typeInfo(T).int.bits);
|
||||
return @as(T, @intCast(std.math.sqrt(@as(UnsignedT, @intCast(sq)))));
|
||||
}
|
||||
return @sqrt(sq);
|
||||
}
|
||||
|
||||
/// Product of all elements. Result has shape {1}; dimension exponent * total.
|
||||
pub fn product(self: *const Self, alloc: Allocator) !Tensor(
|
||||
T,
|
||||
dims.scale(@as(comptime_int, total)).argsOpt(),
|
||||
scales.argsOpt(),
|
||||
&.{1},
|
||||
) {
|
||||
return Tensor(
|
||||
T,
|
||||
dims.scale(@as(comptime_int, total)).argsOpt(),
|
||||
scales.argsOpt(),
|
||||
&.{1},
|
||||
).splat(alloc, @reduce(.Mul, self.data.*));
|
||||
}
|
||||
|
||||
pub fn formatNumber(
|
||||
self: *const Self,
|
||||
writer: *std.Io.Writer,
|
||||
@ -886,247 +1090,305 @@ test "TensorAlloc | Scalar Imperial mass scales" {
|
||||
try std.testing.expectApproxEqAbs(2.5, total.data[0], 1e-6);
|
||||
}
|
||||
|
||||
// // ─── Vector / Tensor tests ────────────────────────────────────────────────
|
||||
//
|
||||
// test "TensorAlloc | Vector initiate" {
|
||||
// const Meter4 = Tensor(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 "TensorAlloc | Vector format" {
|
||||
// const MeterPerSecondSq = Tensor(f32, .{ .L = 1, .T = -2 }, .{ .T = .n }, &.{3});
|
||||
// const KgMeterPerSecond = Tensor(f32, .{ .M = 1, .L = 1, .T = -1 }, .{ .M = .k }, &.{3});
|
||||
//
|
||||
// const accel = MeterPerSecondSq.splat(9.81);
|
||||
// const momentum = KgMeterPerSecond{ .data = .{ 43, 0, 11 } };
|
||||
//
|
||||
// var buf: [64]u8 = undefined;
|
||||
// var res = try std.fmt.bufPrint(&buf, "{d}", .{accel});
|
||||
// try std.testing.expectEqualStrings("(9.81, 9.81, 9.81)m.ns⁻²", res);
|
||||
//
|
||||
// res = try std.fmt.bufPrint(&buf, "{d:.2}", .{momentum});
|
||||
// try std.testing.expectEqualStrings("(43.00, 0.00, 11.00)m.kg.s⁻¹", res);
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector Vec3 Init and Basic Arithmetic" {
|
||||
// const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
//
|
||||
// const v_zero = Meter3.zero;
|
||||
// try std.testing.expectEqual(0, v_zero.data[0]);
|
||||
// try std.testing.expectEqual(0, v_zero.data[2]);
|
||||
//
|
||||
// const v_one = Meter3.one;
|
||||
// try std.testing.expectEqual(1, v_one.data[0]);
|
||||
//
|
||||
// const v_def = Meter3.splat(5);
|
||||
// try std.testing.expectEqual(5, v_def.data[2]);
|
||||
//
|
||||
// const v1 = Meter3{ .data = .{ 10, 20, 30 } };
|
||||
// const v2 = Meter3{ .data = .{ 2, 4, 6 } };
|
||||
//
|
||||
// const added = v1.add(v2);
|
||||
// try std.testing.expectEqual(12, added.data[0]);
|
||||
// try std.testing.expectEqual(24, added.data[1]);
|
||||
// try std.testing.expectEqual(36, added.data[2]);
|
||||
//
|
||||
// const subbed = v1.sub(v2);
|
||||
// try std.testing.expectEqual(8, subbed.data[0]);
|
||||
// try std.testing.expectEqual(16, subbed.data[1]);
|
||||
// try std.testing.expectEqual(24, subbed.data[2]);
|
||||
//
|
||||
// const neg = v1.negate();
|
||||
// try std.testing.expectEqual(-10, neg.data[0]);
|
||||
// try std.testing.expectEqual(-20, neg.data[1]);
|
||||
// try std.testing.expectEqual(-30, neg.data[2]);
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector Kinematics (scalar mul/div broadcast)" {
|
||||
// const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
// const Second1 = Tensor(i32, .{ .T = 1 }, .{}, &.{1});
|
||||
//
|
||||
// const pos = Meter3{ .data = .{ 100, 200, 300 } };
|
||||
// const time = Second1.splat(10);
|
||||
//
|
||||
// const vel = pos.div(time);
|
||||
// try std.testing.expectEqual(10, vel.data[0]);
|
||||
// try std.testing.expectEqual(20, vel.data[1]);
|
||||
// try std.testing.expectEqual(30, vel.data[2]);
|
||||
// try std.testing.expectEqual(1, @TypeOf(vel).dims.get(.L));
|
||||
// try std.testing.expectEqual(-1, @TypeOf(vel).dims.get(.T));
|
||||
//
|
||||
// const new_pos = vel.mul(time);
|
||||
// try std.testing.expectEqual(100, new_pos.data[0]);
|
||||
// try std.testing.expectEqual(0, @TypeOf(new_pos).dims.get(.T));
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector Element-wise Math and Scaling" {
|
||||
// const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
//
|
||||
// const v1 = Meter3{ .data = .{ 10, 20, 30 } };
|
||||
// const v2 = Meter3{ .data = .{ 2, 5, 10 } };
|
||||
// const dv = v1.div(v2);
|
||||
// try std.testing.expectEqual(5, dv.data[0]);
|
||||
// try std.testing.expectEqual(4, dv.data[1]);
|
||||
// try std.testing.expectEqual(3, dv.data[2]);
|
||||
// try std.testing.expectEqual(0, @TypeOf(dv).dims.get(.L));
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector Conversions" {
|
||||
// const KiloMeter3 = Tensor(i32, .{ .L = 1 }, .{ .L = .k }, &.{3});
|
||||
// const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
//
|
||||
// const v_km = KiloMeter3{ .data = .{ 1, 2, 3 } };
|
||||
// const v_m = v_km.to(Meter3);
|
||||
// try std.testing.expectEqual(1000, v_m.data[0]);
|
||||
// try std.testing.expectEqual(2000, v_m.data[1]);
|
||||
// try std.testing.expectEqual(3000, v_m.data[2]);
|
||||
// try std.testing.expectEqual(UnitScale.none, @TypeOf(v_m).scales.get(.L));
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector Length" {
|
||||
// const MeterInt3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
// const MeterFloat3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
//
|
||||
// const v_int = MeterInt3{ .data = .{ 3, 4, 0 } };
|
||||
// try std.testing.expectEqual(25, v_int.lengthSqr());
|
||||
// try std.testing.expectEqual(5, v_int.length());
|
||||
//
|
||||
// const v_float = MeterFloat3{ .data = .{ 3.0, 4.0, 0.0 } };
|
||||
// try std.testing.expectApproxEqAbs(@as(f32, 25.0), v_float.lengthSqr(), 1e-4);
|
||||
// try std.testing.expectApproxEqAbs(@as(f32, 5.0), v_float.length(), 1e-4);
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector Comparisons" {
|
||||
// const Meter3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
// const KiloMeter3 = Tensor(f32, .{ .L = 1 }, .{ .L = .k }, &.{3});
|
||||
//
|
||||
// const v1 = Meter3{ .data = .{ 1000.0, 500.0, 0.0 } };
|
||||
// const v2 = KiloMeter3{ .data = .{ 1.0, 0.5, 0.0 } };
|
||||
// const v3 = KiloMeter3{ .data = .{ 1.0, 0.6, 0.0 } };
|
||||
//
|
||||
// try std.testing.expect(v1.eqAll(v2));
|
||||
// try std.testing.expect(v1.neAll(v3));
|
||||
//
|
||||
// const higher = v3.gt(v1);
|
||||
// try std.testing.expectEqual(false, higher[0]);
|
||||
// try std.testing.expectEqual(true, higher[1]);
|
||||
// try std.testing.expectEqual(false, higher[2]);
|
||||
//
|
||||
// const equal = v3.eq(v1);
|
||||
// try std.testing.expectEqual(true, equal[0]);
|
||||
// try std.testing.expectEqual(false, equal[1]);
|
||||
// try std.testing.expectEqual(true, equal[2]);
|
||||
//
|
||||
// const low_eq = v1.lte(v3);
|
||||
// try std.testing.expect(low_eq[0] and low_eq[1] and low_eq[2]);
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector vs Scalar broadcast comparison" {
|
||||
// const Meter3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
// const KiloMeter1 = Tensor(f32, .{ .L = 1 }, .{ .L = .k }, &.{1});
|
||||
//
|
||||
// const positions = Meter3{ .data = .{ 500.0, 1200.0, 3000.0 } };
|
||||
// const threshold = KiloMeter1.splat(1); // 1 km = 1000 m
|
||||
//
|
||||
// const exceeded = positions.gt(threshold);
|
||||
// try std.testing.expectEqual(false, exceeded[0]);
|
||||
// try std.testing.expectEqual(true, exceeded[1]);
|
||||
// try std.testing.expectEqual(true, exceeded[2]);
|
||||
//
|
||||
// const Meter1 = Tensor(f32, .{ .L = 1 }, .{}, &.{1});
|
||||
// const exact = positions.eq(Meter1.splat(500));
|
||||
// try std.testing.expect(exact[0] == true);
|
||||
// try std.testing.expect(exact[1] == false);
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector contract — dot product (rank-1 * rank-1)" {
|
||||
// const Meter3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
// const Newton3 = Tensor(f32, .{ .M = 1, .L = 1, .T = -2 }, .{}, &.{3});
|
||||
//
|
||||
// const pos = Meter3{ .data = .{ 10.0, 0.0, 0.0 } };
|
||||
// const force = Newton3{ .data = .{ 5.0, 5.0, 0.0 } };
|
||||
//
|
||||
// const work = force.contract(pos, 0, 0);
|
||||
// try std.testing.expectEqual(50.0, work.data[0]);
|
||||
// try std.testing.expectEqual(1, @TypeOf(work).dims.get(.M));
|
||||
// try std.testing.expectEqual(2, @TypeOf(work).dims.get(.L));
|
||||
// try std.testing.expectEqual(-2, @TypeOf(work).dims.get(.T));
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector contract — matrix multiply (rank-2 * rank-2)" {
|
||||
// const A = Tensor(f32, .{}, .{}, &.{ 2, 3 });
|
||||
// const B = Tensor(f32, .{}, .{}, &.{ 3, 2 });
|
||||
//
|
||||
// const a = A{ .data = .{ 1, 2, 3, 4, 5, 6 } };
|
||||
// const b = B{ .data = .{ 7, 8, 9, 10, 11, 12 } };
|
||||
//
|
||||
// const c = a.contract(b, 1, 0);
|
||||
// try std.testing.expectEqual(58, c.data[Tensor(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 0, 0 })]);
|
||||
// try std.testing.expectEqual(64, c.data[Tensor(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 0, 1 })]);
|
||||
// try std.testing.expectEqual(139, c.data[Tensor(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 1, 0 })]);
|
||||
// try std.testing.expectEqual(154, c.data[Tensor(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 1, 1 })]);
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector Abs, Pow, Sqrt and Product" {
|
||||
// const Meter3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
//
|
||||
// const v1 = Meter3{ .data = .{ -2.0, 3.0, -4.0 } };
|
||||
// const v_abs = v1.abs();
|
||||
// try std.testing.expectEqual(2.0, v_abs.data[0]);
|
||||
// try std.testing.expectEqual(4.0, v_abs.data[2]);
|
||||
//
|
||||
// const vol = v_abs.product();
|
||||
// try std.testing.expectEqual(24.0, vol.data[0]);
|
||||
// try std.testing.expectEqual(3, @TypeOf(vol).dims.get(.L));
|
||||
//
|
||||
// const area_vec = v_abs.pow(2);
|
||||
// try std.testing.expectEqual(4.0, area_vec.data[0]);
|
||||
// try std.testing.expectEqual(16.0, area_vec.data[2]);
|
||||
// try std.testing.expectEqual(2, @TypeOf(area_vec).dims.get(.L));
|
||||
//
|
||||
// const sqrted = area_vec.sqrt();
|
||||
// try std.testing.expectEqual(2, sqrted.data[0]);
|
||||
// try std.testing.expectEqual(4, sqrted.data[2]);
|
||||
// try std.testing.expectEqual(1, @TypeOf(sqrted).dims.get(.L));
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Vector eq broadcast on dimensionless" {
|
||||
// const DimLess3 = Tensor(i32, .{}, .{}, &.{3});
|
||||
// const v = DimLess3{ .data = .{ 1, 2, 3 } };
|
||||
//
|
||||
// const eq_res = v.eq(DimLess3.splat(2));
|
||||
// try std.testing.expectEqual(false, eq_res[0]);
|
||||
// try std.testing.expectEqual(true, eq_res[1]);
|
||||
// try std.testing.expectEqual(false, eq_res[2]);
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Tensor idx helper and matrix access" {
|
||||
// const Mat3x3 = Tensor(f32, .{}, .{}, &.{ 3, 3 });
|
||||
// var m: Mat3x3 = Mat3x3.zero;
|
||||
// m.data[Mat3x3.idx(.{ 0, 0 })] = 1.0;
|
||||
// m.data[Mat3x3.idx(.{ 1, 1 })] = 2.0;
|
||||
// m.data[Mat3x3.idx(.{ 2, 2 })] = 3.0;
|
||||
//
|
||||
// try std.testing.expectEqual(1.0, m.data[0]);
|
||||
// try std.testing.expectEqual(2.0, m.data[4]);
|
||||
// try std.testing.expectEqual(3.0, m.data[8]);
|
||||
// try std.testing.expectEqual(0.0, m.data[1]);
|
||||
// }
|
||||
//
|
||||
// test "TensorAlloc | Tensor strides_arr correctness" {
|
||||
// const T1 = Tensor(f32, .{}, .{}, &.{3});
|
||||
// const T2 = Tensor(f32, .{}, .{}, &.{ 3, 4 });
|
||||
// const T3 = Tensor(f32, .{}, .{}, &.{ 2, 3, 4 });
|
||||
//
|
||||
// try std.testing.expectEqual(1, T1.strides_arr[0]);
|
||||
// try std.testing.expectEqual(4, T2.strides_arr[0]);
|
||||
// try std.testing.expectEqual(1, T2.strides_arr[1]);
|
||||
// try std.testing.expectEqual(12, T3.strides_arr[0]);
|
||||
// try std.testing.expectEqual(4, T3.strides_arr[1]);
|
||||
// try std.testing.expectEqual(1, T3.strides_arr[2]);
|
||||
// }
|
||||
// ─── Vector / Tensor tests ────────────────────────────────────────────────
|
||||
|
||||
test "TensorAlloc | Vector initiate" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Meter4 = Tensor(f32, .{ .L = 1 }, .{}, &.{4});
|
||||
const m = try Meter4.splat(alloc, 1);
|
||||
try std.testing.expect(m.data[0] == 1);
|
||||
try std.testing.expect(m.data[3] == 1);
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector format" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const MeterPerSecondSq = Tensor(f32, .{ .L = 1, .T = -2 }, .{ .T = .n }, &.{3});
|
||||
const KgMeterPerSecond = Tensor(f32, .{ .M = 1, .L = 1, .T = -1 }, .{ .M = .k }, &.{3});
|
||||
|
||||
const accel = try MeterPerSecondSq.splat(alloc, 9.81);
|
||||
const momentum = try KgMeterPerSecond.load(alloc, &.{ 43, 0, 11 });
|
||||
|
||||
var buf: [64]u8 = undefined;
|
||||
var res = try std.fmt.bufPrint(&buf, "{d}", .{accel});
|
||||
try std.testing.expectEqualStrings("(9.81, 9.81, 9.81)m.ns⁻²", res);
|
||||
|
||||
res = try std.fmt.bufPrint(&buf, "{d:.2}", .{momentum});
|
||||
try std.testing.expectEqualStrings("(43.00, 0.00, 11.00)m.kg.s⁻¹", res);
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector Vec3 Init and Basic Arithmetic" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
|
||||
const v_zero = try Meter3.splat(alloc, 0);
|
||||
try std.testing.expectEqual(0, v_zero.data[0]);
|
||||
try std.testing.expectEqual(0, v_zero.data[2]);
|
||||
|
||||
const v_one = try Meter3.splat(alloc, 1);
|
||||
try std.testing.expectEqual(1, v_one.data[0]);
|
||||
|
||||
const v_def = try Meter3.splat(alloc, 5);
|
||||
try std.testing.expectEqual(5, v_def.data[2]);
|
||||
|
||||
const v1 = try Meter3.load(alloc, &.{ 10, 20, 30 });
|
||||
const v2 = try Meter3.load(alloc, &.{ 2, 4, 6 });
|
||||
|
||||
const added = try v1.add(alloc, v2);
|
||||
try std.testing.expectEqual(12, added.data[0]);
|
||||
try std.testing.expectEqual(24, added.data[1]);
|
||||
try std.testing.expectEqual(36, added.data[2]);
|
||||
|
||||
const subbed = try v1.sub(alloc, v2);
|
||||
try std.testing.expectEqual(8, subbed.data[0]);
|
||||
try std.testing.expectEqual(16, subbed.data[1]);
|
||||
try std.testing.expectEqual(24, subbed.data[2]);
|
||||
|
||||
const neg = try v1.negate(alloc);
|
||||
try std.testing.expectEqual(-10, neg.data[0]);
|
||||
try std.testing.expectEqual(-20, neg.data[1]);
|
||||
try std.testing.expectEqual(-30, neg.data[2]);
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector Kinematics (scalar mul/div broadcast)" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
const Second1 = Tensor(i32, .{ .T = 1 }, .{}, &.{1});
|
||||
|
||||
const pos = try Meter3.load(alloc, &.{ 100, 200, 300 });
|
||||
const time = try Second1.splat(alloc, 10);
|
||||
|
||||
const vel = try pos.div(alloc, time);
|
||||
try std.testing.expectEqual(10, vel.data[0]);
|
||||
try std.testing.expectEqual(20, vel.data[1]);
|
||||
try std.testing.expectEqual(30, vel.data[2]);
|
||||
try std.testing.expectEqual(1, @TypeOf(vel).dims.get(.L));
|
||||
try std.testing.expectEqual(-1, @TypeOf(vel).dims.get(.T));
|
||||
|
||||
const new_pos = try vel.mul(alloc, time);
|
||||
try std.testing.expectEqual(100, new_pos.data[0]);
|
||||
try std.testing.expectEqual(0, @TypeOf(new_pos).dims.get(.T));
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector Element-wise Math and Scaling" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
|
||||
const v1 = try Meter3.load(alloc, &.{ 10, 20, 30 });
|
||||
const v2 = try Meter3.load(alloc, &.{ 2, 5, 10 });
|
||||
|
||||
const dv = try v1.div(alloc, v2);
|
||||
try std.testing.expectEqual(5, dv.data[0]);
|
||||
try std.testing.expectEqual(4, dv.data[1]);
|
||||
try std.testing.expectEqual(3, dv.data[2]);
|
||||
try std.testing.expectEqual(0, @TypeOf(dv).dims.get(.L));
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector Conversions" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const KiloMeter3 = Tensor(i32, .{ .L = 1 }, .{ .L = .k }, &.{3});
|
||||
const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
|
||||
const v_km = try KiloMeter3.load(alloc, &.{ 1, 2, 3 });
|
||||
const v_m = try v_km.to(alloc, Meter3);
|
||||
|
||||
try std.testing.expectEqual(1000, v_m.data[0]);
|
||||
try std.testing.expectEqual(2000, v_m.data[1]);
|
||||
try std.testing.expectEqual(3000, v_m.data[2]);
|
||||
try std.testing.expectEqual(UnitScale.none, @TypeOf(v_m).scales.get(.L));
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector Length" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const MeterInt3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3});
|
||||
const MeterFloat3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
|
||||
const v_int = try MeterInt3.load(alloc, &.{ 3, 4, 0 });
|
||||
try std.testing.expectEqual(25, v_int.lengthSqr());
|
||||
try std.testing.expectEqual(5, v_int.length());
|
||||
|
||||
const v_float = try MeterFloat3.load(alloc, &.{ 3.0, 4.0, 0.0 });
|
||||
try std.testing.expectApproxEqAbs(@as(f32, 25.0), v_float.lengthSqr(), 1e-4);
|
||||
try std.testing.expectApproxEqAbs(@as(f32, 5.0), v_float.length(), 1e-4);
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector Comparisons" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Meter3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
const KiloMeter3 = Tensor(f32, .{ .L = 1 }, .{ .L = .k }, &.{3});
|
||||
|
||||
const v1 = try Meter3.load(alloc, &.{ 1000.0, 500.0, 0.0 });
|
||||
const v2 = try KiloMeter3.load(alloc, &.{ 1.0, 0.5, 0.0 });
|
||||
const v3 = try KiloMeter3.load(alloc, &.{ 1.0, 0.6, 0.0 });
|
||||
|
||||
try std.testing.expect(try v1.eqAll(alloc, v2));
|
||||
try std.testing.expect(try v1.neAll(alloc, v3));
|
||||
|
||||
const higher = try v3.gt(alloc, v1);
|
||||
try std.testing.expectEqual(false, higher[0]);
|
||||
try std.testing.expectEqual(true, higher[1]);
|
||||
try std.testing.expectEqual(false, higher[2]);
|
||||
|
||||
const equal = try v3.eq(alloc, v1);
|
||||
try std.testing.expectEqual(true, equal[0]);
|
||||
try std.testing.expectEqual(false, equal[1]);
|
||||
try std.testing.expectEqual(true, equal[2]);
|
||||
|
||||
const low_eq = try v1.lte(alloc, v3);
|
||||
try std.testing.expect(low_eq[0] and low_eq[1] and low_eq[2]);
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector vs Scalar broadcast comparison" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Meter3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
const KiloMeter1 = Tensor(f32, .{ .L = 1 }, .{ .L = .k }, &.{1});
|
||||
|
||||
const positions = try Meter3.load(alloc, &.{ 500.0, 1200.0, 3000.0 });
|
||||
const threshold = try KiloMeter1.splat(alloc, 1); // 1 km = 1000 m
|
||||
|
||||
const exceeded = try positions.gt(alloc, threshold);
|
||||
try std.testing.expectEqual(false, exceeded[0]);
|
||||
try std.testing.expectEqual(true, exceeded[1]);
|
||||
try std.testing.expectEqual(true, exceeded[2]);
|
||||
|
||||
const Meter1 = Tensor(f32, .{ .L = 1 }, .{}, &.{1});
|
||||
const exact = try positions.eq(alloc, try Meter1.splat(alloc, 500));
|
||||
try std.testing.expect(exact[0] == true);
|
||||
try std.testing.expect(exact[1] == false);
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector contract — dot product (rank-1 * rank-1)" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Meter3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
const Newton3 = Tensor(f32, .{ .M = 1, .L = 1, .T = -2 }, .{}, &.{3});
|
||||
|
||||
const pos = try Meter3.load(alloc, &.{ 10.0, 0.0, 0.0 });
|
||||
const force = try Newton3.load(alloc, &.{ 5.0, 5.0, 0.0 });
|
||||
|
||||
const work = try force.contract(alloc, pos, 0, 0);
|
||||
try std.testing.expectEqual(50.0, work.data[0]);
|
||||
try std.testing.expectEqual(1, @TypeOf(work).dims.get(.M));
|
||||
try std.testing.expectEqual(2, @TypeOf(work).dims.get(.L));
|
||||
try std.testing.expectEqual(-2, @TypeOf(work).dims.get(.T));
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector contract — matrix multiply (rank-2 * rank-2)" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const A = Tensor(f32, .{}, .{}, &.{ 2, 3 });
|
||||
const B = Tensor(f32, .{}, .{}, &.{ 3, 2 });
|
||||
|
||||
const a = try A.load(alloc, &.{ 1, 2, 3, 4, 5, 6 });
|
||||
const b = try B.load(alloc, &.{ 7, 8, 9, 10, 11, 12 });
|
||||
|
||||
const c = try a.contract(alloc, b, 1, 0);
|
||||
try std.testing.expectEqual(58, c.data[Tensor(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 0, 0 })]);
|
||||
try std.testing.expectEqual(64, c.data[Tensor(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 0, 1 })]);
|
||||
try std.testing.expectEqual(139, c.data[Tensor(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 1, 0 })]);
|
||||
try std.testing.expectEqual(154, c.data[Tensor(f32, .{}, .{}, &.{ 2, 2 }).idx(.{ 1, 1 })]);
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector Abs, Pow, Sqrt and Product" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Meter3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3});
|
||||
|
||||
const v1 = try Meter3.load(alloc, &.{ -2.0, 3.0, -4.0 });
|
||||
const v_abs = try v1.abs(alloc);
|
||||
try std.testing.expectEqual(2.0, v_abs.data[0]);
|
||||
try std.testing.expectEqual(4.0, v_abs.data[2]);
|
||||
|
||||
const vol = try v_abs.product(alloc);
|
||||
try std.testing.expectEqual(24.0, vol.data[0]);
|
||||
try std.testing.expectEqual(3, @TypeOf(vol).dims.get(.L));
|
||||
|
||||
const area_vec = try v_abs.pow(alloc, 2);
|
||||
try std.testing.expectEqual(4.0, area_vec.data[0]);
|
||||
try std.testing.expectEqual(16.0, area_vec.data[2]);
|
||||
try std.testing.expectEqual(2, @TypeOf(area_vec).dims.get(.L));
|
||||
|
||||
const sqrted = try area_vec.sqrt(alloc);
|
||||
try std.testing.expectEqual(2, sqrted.data[0]);
|
||||
try std.testing.expectEqual(4, sqrted.data[2]);
|
||||
try std.testing.expectEqual(1, @TypeOf(sqrted).dims.get(.L));
|
||||
}
|
||||
|
||||
test "TensorAlloc | Vector eq broadcast on dimensionless" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const DimLess3 = Tensor(i32, .{}, .{}, &.{3});
|
||||
const v = try DimLess3.load(alloc, &.{ 1, 2, 3 });
|
||||
|
||||
const eq_res = try v.eq(alloc, try DimLess3.splat(alloc, 2));
|
||||
try std.testing.expectEqual(false, eq_res[0]);
|
||||
try std.testing.expectEqual(true, eq_res[1]);
|
||||
try std.testing.expectEqual(false, eq_res[2]);
|
||||
}
|
||||
|
||||
test "TensorAlloc | Tensor idx helper and matrix access" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const Mat3x3 = Tensor(f32, .{}, .{}, &.{ 3, 3 });
|
||||
var m = try Mat3x3.splat(alloc, 0);
|
||||
m.data[Mat3x3.idx(.{ 0, 0 })] = 1;
|
||||
m.data[Mat3x3.idx(.{ 1, 1 })] = 2;
|
||||
m.data[Mat3x3.idx(.{ 2, 2 })] = 3;
|
||||
|
||||
try std.testing.expectEqual(1.0, m.data[0]);
|
||||
try std.testing.expectEqual(2.0, m.data[4]);
|
||||
try std.testing.expectEqual(3.0, m.data[8]);
|
||||
try std.testing.expectEqual(0.0, m.data[1]);
|
||||
}
|
||||
|
||||
test "TensorAlloc | Tensor strides_arr correctness" {
|
||||
const T1 = Tensor(f32, .{}, .{}, &.{3});
|
||||
const T2 = Tensor(f32, .{}, .{}, &.{ 3, 4 });
|
||||
const T3 = Tensor(f32, .{}, .{}, &.{ 2, 3, 4 });
|
||||
|
||||
try std.testing.expectEqual(1, T1.strides_arr[0]);
|
||||
try std.testing.expectEqual(4, T2.strides_arr[0]);
|
||||
try std.testing.expectEqual(1, T2.strides_arr[1]);
|
||||
try std.testing.expectEqual(12, T3.strides_arr[0]);
|
||||
try std.testing.expectEqual(4, T3.strides_arr[1]);
|
||||
try std.testing.expectEqual(1, T3.strides_arr[2]);
|
||||
}
|
||||
//
|
||||
// test "TensorAlloc | Slice 1D basic" {
|
||||
// const Vec = Tensor(i32, .{}, .{}, &.{5});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user