Working other base operation (sub, mul, div, ect)

This commit is contained in:
adrien 2026-05-25 01:52:12 +02:00
parent 7494595db4
commit 0ef19e18de

View File

@ -98,6 +98,196 @@ pub fn Tensor(
return TargetType{ .data = vec_ptr }; return TargetType{ .data = vec_ptr };
} }
/// Element-wise sub. Dimensions must match; scales resolve to finer.
/// RHS must have the same shape as self, or total == 1 (broadcast).
pub inline fn sub(self: *const Self, alloc: Allocator, rhs: anytype) !Tensor(
T,
dims.argsOpt(),
sh.finerScales(Self, @TypeOf(rhs)).argsOpt(),
shape,
) {
const RhsType = @TypeOf(rhs);
if (comptime !sh.isTensor(RhsType))
@compileError("rhs can only be a Tensor ");
if (comptime !dims.eql(RhsType.dims))
@compileError("Dimension mismatch in sub: " ++ dims.str() ++ " vs " ++ RhsType.dims.str());
if (comptime RhsType.total != 1 and !sh.shapeEql(shape, RhsType.shape))
@compileError("Shape mismatch in sub: element-wise operations require identical shapes, or a scalar RHS.");
const TargetType = Tensor(T, dims.argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape);
const l: TargetType = try self.to(alloc, TargetType);
defer l.deinit(alloc);
const r: TargetType = try rhs.to(alloc, TargetType);
defer r.deinit(alloc);
const result_vec = if (comptime sh.isInt(T))
l.data.* -| r.data.*
else
l.data.* - r.data.*;
const vec_ptr = try alloc.create(@TypeOf(result_vec));
vec_ptr.* = result_vec;
return TargetType{ .data = vec_ptr };
}
/// Element-wise multiply. Dimension exponents summed.
/// Shape {1} RHS is automatically broadcast across all elements.
pub inline fn mul(self: *const Self, alloc: Allocator, rhs: anytype) !Tensor(
T,
dims.add(@TypeOf(rhs).dims).argsOpt(),
sh.finerScales(Self, @TypeOf(rhs)).argsOpt(),
shape,
) {
const RhsType = @TypeOf(rhs);
if (comptime !sh.isTensor(RhsType))
@compileError("rhs can only be a Tensor ");
if (comptime RhsType.total != 1 and !sh.shapeEql(shape, RhsType.shape))
@compileError("Shape mismatch in mul: element-wise operations require identical shapes, or a scalar RHS.");
const SelfNorm = Tensor(T, dims.argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape);
const RhsNorm = Tensor(T, RhsType.dims.argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape);
const TargetType = Tensor(T, dims.add(RhsType.dims).argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape);
const l: SelfNorm = try self.to(alloc, SelfNorm);
defer l.deinit(alloc);
const r: RhsNorm = try rhs.to(alloc, RhsNorm);
defer r.deinit(alloc);
const result_vec = if (comptime sh.isInt(T))
l.data.* *| r.data.*
else
l.data.* * r.data.*;
const vec_ptr = try alloc.create(@TypeOf(result_vec));
vec_ptr.* = result_vec;
return TargetType{ .data = vec_ptr };
}
/// Element-wise divide. Dimension exponents subtracted.
/// Shape {1} RHS is automatically broadcast across all elements.
pub inline fn div(self: *const Self, alloc: Allocator, rhs: anytype) !Tensor(
T,
dims.sub(@TypeOf(rhs).dims).argsOpt(),
sh.finerScales(Self, @TypeOf(rhs)).argsOpt(),
shape,
) {
const RhsType = @TypeOf(rhs);
if (comptime !sh.isTensor(RhsType))
@compileError("rhs can only be a Tensor ");
if (comptime RhsType.total != 1 and !sh.shapeEql(shape, RhsType.shape))
@compileError("Shape mismatch in div: element-wise operations require identical shapes, or a scalar RHS.");
const SelfNorm = Tensor(T, dims.argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape);
const RhsNorm = Tensor(T, RhsType.dims.argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape);
const TargetType = Tensor(T, dims.sub(RhsType.dims).argsOpt(), sh.finerScales(Self, RhsType).argsOpt(), shape);
const l: SelfNorm = try self.to(alloc, SelfNorm);
defer l.deinit(alloc);
const r: RhsNorm = try rhs.to(alloc, RhsNorm);
defer r.deinit(alloc);
const result_vec = if (comptime sh.isInt(T))
@divTrunc(l.data.*, r.data.*)
else
l.data.* / r.data.*;
const vec_ptr = try alloc.create(@TypeOf(result_vec));
vec_ptr.* = result_vec;
return TargetType{ .data = vec_ptr };
}
/// Absolute value of every element.
pub inline fn abs(self: *const Self, alloc: Allocator) !Self {
const result_vec = @as(Vec, @bitCast(@abs(self.data.*)));
const vec_ptr = try alloc.create(@TypeOf(result_vec));
vec_ptr.* = result_vec;
return Self{ .data = vec_ptr };
}
/// Raise every element to a comptime integer exponent.
pub inline fn pow(self: *const Self, alloc: Allocator, comptime exp: comptime_int) !Tensor(
T,
dims.scale(exp).argsOpt(),
scales.argsOpt(),
shape,
) {
if (comptime exp < 0) @compileError("Pow only support exp >= 0");
const TargetType = Tensor(T, dims.scale(exp).argsOpt(), scales.argsOpt(), shape);
if (comptime exp == 0) {
const result_vec: Vec = @splat(1);
const vec_ptr = try alloc.create(@TypeOf(result_vec));
vec_ptr.* = result_vec;
return TargetType{ .data = vec_ptr };
}
if (comptime exp == 1) {
// Copy allocation to ensure `.deinit(alloc)` works cleanly for the caller
const vec_ptr = try alloc.create(Vec);
vec_ptr.* = self.data.*;
return TargetType{ .data = vec_ptr };
}
var data: Vec = self.data.*;
for (0..exp - 1) |_|
data = data * self.data.*;
const vec_ptr = try alloc.create(@TypeOf(data));
vec_ptr.* = data;
return TargetType{ .data = vec_ptr };
}
/// Square root of every element. All dimension exponents must be even.
pub inline fn sqrt(self: *const Self, alloc: Allocator) !Tensor(
T,
dims.div(2).argsOpt(),
scales.argsOpt(),
shape,
) {
if (comptime !dims.isSquare())
@compileError("Cannot take sqrt of " ++ dims.str() ++ ": exponents must be even.");
const TargetType = Tensor(T, dims.div(2).argsOpt(), scales.argsOpt(), shape);
if (comptime @typeInfo(T) == .float) {
const result_vec = @sqrt(self.data.*);
const vec_ptr = try alloc.create(@TypeOf(result_vec));
vec_ptr.* = result_vec;
return TargetType{ .data = vec_ptr };
}
const arr: [total]T = self.data.*;
var res_arr: [total]T = undefined;
const UnsignedT = @Int(.unsigned, @typeInfo(T).int.bits);
for (0..total) |i| {
const v = arr[i];
res_arr[i] = if (v < 0) 0 else @as(T, @intCast(std.math.sqrt(@as(UnsignedT, @intCast(v)))));
}
const vec_ptr = try alloc.create(Vec);
vec_ptr.* = res_arr;
return TargetType{ .data = vec_ptr };
}
/// Negate every element.
pub inline fn negate(self: *const Self, alloc: Allocator) !Self {
const result_vec = -self.data.*;
const vec_ptr = try alloc.create(@TypeOf(result_vec));
vec_ptr.* = result_vec;
return Self{ .data = vec_ptr };
}
/// Convert to a compatible Tensor type. /// Convert to a compatible Tensor type.
/// Dimension mismatch compile error. /// Dimension mismatch compile error.
/// Dest.shape must equal self.shape, or total == 1 -> splat to Dest shape (scalar pattern). /// Dest.shape must equal self.shape, or total == 1 -> splat to Dest shape (scalar pattern).
@ -349,165 +539,221 @@ test "TensorAlloc | Scalar Add" {
const added2 = try distance.add(alloc, distance3); const added2 = try distance.add(alloc, distance3);
try std.testing.expectEqual(2010, added2.data[0]); try std.testing.expectEqual(2010, added2.data[0]);
const added3 = try (try distance3.add(alloc, distance)).to(alloc, KiloMeter); const added3_tmp = try distance3.add(alloc, distance);
const added3 = try added3_tmp.to(alloc, KiloMeter);
try std.testing.expectEqual(2, added3.data[0]); try std.testing.expectEqual(2, added3.data[0]);
const distance4 = try KiloMeter_f.splat(alloc, 2); const distance4 = try KiloMeter_f.splat(alloc, 2);
const added4 = try (try distance4.add(alloc, distance)).to(alloc, KiloMeter_f); const added4_tmp = try distance4.add(alloc, distance);
const added4 = try added4_tmp.to(alloc, KiloMeter_f);
try std.testing.expectApproxEqAbs(2.01, added4.data[0], 0.000001); try std.testing.expectApproxEqAbs(2.01, added4.data[0], 0.000001);
} }
// test "TensorAlloc | Scalar Sub" { test "TensorAlloc | Scalar Sub" {
// const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1}); var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// const KiloMeter_f = Tensor(f64, .{ .L = 1 }, .{ .L = .k }, &.{1}); defer arena.deinit();
// const alloc = arena.allocator();
// const a = Meter.splat(500);
// const b = Meter.splat(200); const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1});
// const diff = a.sub(b); const KiloMeter_f = Tensor(f64, .{ .L = 1 }, .{ .L = .k }, &.{1});
// try std.testing.expectEqual(300, diff.data[0]);
// const diff2 = b.sub(a); const a = try Meter.splat(alloc, 500);
// try std.testing.expectEqual(-300, diff2.data[0]); const b = try Meter.splat(alloc, 200);
// const diff = try a.sub(alloc, b);
// const km_f = KiloMeter_f.splat(2.5); try std.testing.expectEqual(300, diff.data[0]);
// const m_f = Meter.splat(500); const diff2 = try b.sub(alloc, a);
// const diff3 = km_f.sub(m_f); try std.testing.expectEqual(-300, diff2.data[0]);
// try std.testing.expectApproxEqAbs(2000, diff3.data[0], 1e-4);
// } const km_f = try KiloMeter_f.splat(alloc, 2.5);
// const m_f = try Meter.splat(alloc, 500);
// test "TensorAlloc | Scalar MulBy" { const diff3 = try km_f.sub(alloc, m_f);
// const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1}); try std.testing.expectApproxEqAbs(2000.0, diff3.data[0], 1e-4);
// const Second = Tensor(f32, .{ .T = 1 }, .{}, &.{1}); }
//
// const d = Meter.splat(3); test "TensorAlloc | Scalar MulBy" {
// const t = Second.splat(4); var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// const at = d.mul(t); defer arena.deinit();
// try std.testing.expectEqual(12, at.data[0]); const alloc = arena.allocator();
// try std.testing.expectEqual(1, @TypeOf(at).dims.get(.L));
// try std.testing.expectEqual(1, @TypeOf(at).dims.get(.T)); const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1});
// const Second = Tensor(f32, .{ .T = 1 }, .{}, &.{1});
// const d2 = Meter.splat(5);
// const area = d.mul(d2); const d = try Meter.splat(alloc, 3);
// try std.testing.expectEqual(15, area.data[0]); const t = try Second.splat(alloc, 4);
// try std.testing.expectEqual(2, @TypeOf(area).dims.get(.L)); const at = try d.mul(alloc, t);
// } try std.testing.expectEqual(12, at.data[0]);
// try std.testing.expectEqual(1, @TypeOf(at).dims.get(.L));
// test "TensorAlloc | Scalar MulBy with scale" { try std.testing.expectEqual(1, @TypeOf(at).dims.get(.T));
// const KiloMeter = Tensor(f32, .{ .L = 1 }, .{ .L = .k }, &.{1});
// const KiloGram = Tensor(f32, .{ .M = 1 }, .{ .M = .k }, &.{1}); const d2 = try Meter.splat(alloc, 5);
// const area = try d.mul(alloc, d2);
// const dist = KiloMeter.splat(2.0); try std.testing.expectEqual(15, area.data[0]);
// const mass = KiloGram.splat(3.0); try std.testing.expectEqual(2, @TypeOf(area).dims.get(.L));
// const prod = dist.mul(mass); }
// try std.testing.expectEqual(1, @TypeOf(prod).dims.get(.L));
// try std.testing.expectEqual(1, @TypeOf(prod).dims.get(.M)); test "TensorAlloc | Scalar MulBy with scale" {
// } var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// defer arena.deinit();
// test "TensorAlloc | Scalar MulBy with type change" { const alloc = arena.allocator();
// const Meter = Tensor(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
// const Second = Tensor(f64, .{ .T = 1 }, .{}, &.{1}); const KiloMeter = Tensor(f32, .{ .L = 1 }, .{ .L = .k }, &.{1});
// const KmSec = Tensor(i64, .{ .L = 1, .T = 1 }, .{ .L = .k }, &.{1}); const KiloGram = Tensor(f32, .{ .M = 1 }, .{ .M = .k }, &.{1});
// const KmSec_f = Tensor(f32, .{ .L = 1, .T = 1 }, .{ .L = .k }, &.{1});
// const dist = try KiloMeter.splat(alloc, 2.0);
// const d = Meter.splat(3); const mass = try KiloGram.splat(alloc, 3.0);
// const t = Second.splat(4); const prod = try dist.mul(alloc, mass);
// try std.testing.expectEqual(1, @TypeOf(prod).dims.get(.L));
// try std.testing.expectEqual(12, d.mul(t).to(KmSec).data[0]); try std.testing.expectEqual(1, @TypeOf(prod).dims.get(.M));
// try std.testing.expectApproxEqAbs(12.0, d.mul(t).to(KmSec_f).data[0], 0.0001); }
// }
// test "TensorAlloc | Scalar MulBy with type change" {
// test "TensorAlloc | Scalar MulBy small" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// const Meter = Tensor(i128, .{ .L = 1 }, .{ .L = .n }, &.{1}); defer arena.deinit();
// const Second = Tensor(f32, .{ .T = 1 }, .{}, &.{1}); const alloc = arena.allocator();
// const d = Meter.splat(3);
// const t = Second.splat(4); const Meter = Tensor(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
// try std.testing.expectEqual(12, d.mul(t).data[0]); const Second = Tensor(f64, .{ .T = 1 }, .{}, &.{1});
// } const KmSec = Tensor(i64, .{ .L = 1, .T = 1 }, .{ .L = .k }, &.{1});
// const KmSec_f = Tensor(f32, .{ .L = 1, .T = 1 }, .{ .L = .k }, &.{1});
// test "TensorAlloc | Scalar MulBy dimensionless" {
// const DimLess = Tensor(i128, .{}, .{}, &.{1}); const d = try Meter.splat(alloc, 3);
// const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1}); const t = try Second.splat(alloc, 4);
// const d = Meter.splat(7); const dt_prod = try d.mul(alloc, t);
// const scaled = d.mul(DimLess.splat(3));
// try std.testing.expectEqual(21, scaled.data[0]); const kmsec_val = try dt_prod.to(alloc, KmSec);
// } try std.testing.expectEqual(12, kmsec_val.data[0]);
//
// test "TensorAlloc | Scalar Sqrt" { const kmsec_f_val = try dt_prod.to(alloc, KmSec_f);
// const MeterSquare = Tensor(i128, .{ .L = 2 }, .{}, &.{1}); try std.testing.expectApproxEqAbs(12.0, kmsec_f_val.data[0], 0.0001);
// const MeterSquare_f = Tensor(f64, .{ .L = 2 }, .{}, &.{1}); }
//
// var d = MeterSquare.splat(9); test "TensorAlloc | Scalar MulBy small" {
// var scaled = d.sqrt(); var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// try std.testing.expectEqual(3, scaled.data[0]); defer arena.deinit();
// try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L)); const alloc = arena.allocator();
//
// d = MeterSquare.splat(-5); const Meter = Tensor(i128, .{ .L = 1 }, .{ .L = .n }, &.{1});
// scaled = d.sqrt(); const Second = Tensor(f32, .{ .T = 1 }, .{}, &.{1});
// try std.testing.expectEqual(0, scaled.data[0]); const d = try Meter.splat(alloc, 3);
// const t = try Second.splat(alloc, 4);
// const d2 = MeterSquare_f.splat(20); const dt = try d.mul(alloc, t);
// const scaled2 = d2.sqrt(); try std.testing.expectEqual(12, dt.data[0]);
// try std.testing.expectApproxEqAbs(4.472135955, scaled2.data[0], 1e-4); }
// }
// test "TensorAlloc | Scalar MulBy dimensionless" {
// test "TensorAlloc | Scalar Chained: velocity and acceleration" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1}); defer arena.deinit();
// const Second = Tensor(f32, .{ .T = 1 }, .{}, &.{1}); const alloc = arena.allocator();
//
// const dist = Meter.splat(100); const DimLess = Tensor(i128, .{}, .{}, &.{1});
// const t1 = Second.splat(5); const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1});
// const velocity = dist.div(t1); const d = try Meter.splat(alloc, 7);
// try std.testing.expectEqual(20, velocity.data[0]); const dl = try DimLess.splat(alloc, 3);
// const scaled = try d.mul(alloc, dl);
// const t2 = Second.splat(4); try std.testing.expectEqual(21, scaled.data[0]);
// const accel = velocity.div(t2); }
// try std.testing.expectEqual(5, accel.data[0]);
// } test "TensorAlloc | Scalar Sqrt" {
// var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// test "TensorAlloc | Scalar DivBy integer exact" { defer arena.deinit();
// const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1}); const alloc = arena.allocator();
// const Second = Tensor(f32, .{ .T = 1 }, .{}, &.{1});
// const MeterSquare = Tensor(i128, .{ .L = 2 }, .{}, &.{1});
// const dist = Meter.splat(120); const MeterSquare_f = Tensor(f64, .{ .L = 2 }, .{}, &.{1});
// const time = Second.splat(4);
// const vel = dist.div(time); var d = try MeterSquare.splat(alloc, 9);
// try std.testing.expectEqual(30, vel.data[0]); var scaled = try d.sqrt(alloc);
// } try std.testing.expectEqual(3, scaled.data[0]);
// try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L));
// test "TensorAlloc | Scalar Finer scales skip dim 0" {
// const Dimless = Tensor(i128, .{}, .{}, &.{1}); d = try MeterSquare.splat(alloc, -5);
// const KiloMetre = Tensor(i128, .{ .L = 1 }, .{ .L = .k }, &.{1}); scaled = try d.sqrt(alloc);
// try std.testing.expectEqual(0, scaled.data[0]);
// const r = Dimless.splat(30);
// const km = KiloMetre.splat(4); const d2 = try MeterSquare_f.splat(alloc, 20);
// const vel = r.mul(km); const scaled2 = try d2.sqrt(alloc);
// try std.testing.expectEqual(120, vel.data[0]); try std.testing.expectApproxEqAbs(4.472135955, scaled2.data[0], 1e-4);
// try std.testing.expectEqual(Scales.UnitScale.k, @TypeOf(vel).scales.get(.L)); }
// }
// test "TensorAlloc | Scalar Chained: velocity and acceleration" {
// test "TensorAlloc | Scalar Conversion chain: km -> m -> cm" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// const KiloMeter = Tensor(i128, .{ .L = 1 }, .{ .L = .k }, &.{1}); defer arena.deinit();
// const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1}); const alloc = arena.allocator();
// const CentiMeter = Tensor(i128, .{ .L = 1 }, .{ .L = .c }, &.{1});
// const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1});
// const km = KiloMeter.splat(15); const Second = Tensor(f32, .{ .T = 1 }, .{}, &.{1});
// const m = km.to(Meter);
// const cm = m.to(CentiMeter); const dist = try Meter.splat(alloc, 100);
// try std.testing.expectEqual(15_000, m.data[0]); const t1 = try Second.splat(alloc, 5);
// try std.testing.expectEqual(1_500_000, cm.data[0]); const velocity = try dist.div(alloc, t1);
// } try std.testing.expectEqual(20, velocity.data[0]);
//
// test "TensorAlloc | Scalar Conversion: hours -> minutes -> seconds" { const t2 = try Second.splat(alloc, 4);
// const Hour = Tensor(i128, .{ .T = 1 }, .{ .T = .hour }, &.{1}); const accel = try velocity.div(alloc, t2);
// const Minute = Tensor(i128, .{ .T = 1 }, .{ .T = .min }, &.{1}); try std.testing.expectEqual(5, accel.data[0]);
// const Second = Tensor(i128, .{ .T = 1 }, .{}, &.{1}); }
//
// const h = Hour.splat(1); test "TensorAlloc | Scalar DivBy integer exact" {
// const min = h.to(Minute); var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
// const sec = min.to(Second); defer arena.deinit();
// try std.testing.expectEqual(60, min.data[0]); const alloc = arena.allocator();
// try std.testing.expectEqual(3600, sec.data[0]);
// } const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1});
// const Second = Tensor(f32, .{ .T = 1 }, .{}, &.{1});
const dist = try Meter.splat(alloc, 120);
const time = try Second.splat(alloc, 4);
const vel = try dist.div(alloc, time);
try std.testing.expectEqual(30, vel.data[0]);
}
test "TensorAlloc | Scalar Finer scales skip dim 0" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
const Dimless = Tensor(i128, .{}, .{}, &.{1});
const KiloMetre = Tensor(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
const r = try Dimless.splat(alloc, 30);
const km = try KiloMetre.splat(alloc, 4);
const vel = try r.mul(alloc, km);
try std.testing.expectEqual(120, vel.data[0]);
try std.testing.expectEqual(Scales.UnitScale.k, @TypeOf(vel).scales.get(.L));
}
test "TensorAlloc | Scalar Conversion chain: km -> m -> cm" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
const KiloMeter = Tensor(i128, .{ .L = 1 }, .{ .L = .k }, &.{1});
const Meter = Tensor(i128, .{ .L = 1 }, .{}, &.{1});
const CentiMeter = Tensor(i128, .{ .L = 1 }, .{ .L = .c }, &.{1});
const km = try KiloMeter.splat(alloc, 15);
const m = try km.to(alloc, Meter);
const cm = try m.to(alloc, CentiMeter);
try std.testing.expectEqual(15_000, m.data[0]);
try std.testing.expectEqual(1_500_000, cm.data[0]);
}
test "TensorAlloc | Scalar Conversion: hours -> minutes -> seconds" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
const Hour = Tensor(i128, .{ .T = 1 }, .{ .T = .hour }, &.{1});
const Minute = Tensor(i128, .{ .T = 1 }, .{ .T = .min }, &.{1});
const Second = Tensor(i128, .{ .T = 1 }, .{}, &.{1});
const h = try Hour.splat(alloc, 1);
const min = try h.to(alloc, Minute);
const sec = try min.to(alloc, Second);
try std.testing.expectEqual(60, min.data[0]);
try std.testing.expectEqual(3600, sec.data[0]);
}
// test "TensorAlloc | Scalar Format" { // test "TensorAlloc | Scalar Format" {
// const MeterPerSecondSq = Tensor(f32, .{ .L = 1, .T = -2 }, .{ .T = .n }, &.{1}); // const MeterPerSecondSq = Tensor(f32, .{ .L = 1, .T = -2 }, .{ .T = .n }, &.{1});
// const Meter = Tensor(f32, .{ .L = 1 }, .{}, &.{1}); // const Meter = Tensor(f32, .{ .L = 1 }, .{}, &.{1});