From 9635cfb481ab0d2dea83984eabb0b74185ac7b61 Mon Sep 17 00:00:00 2001 From: adrien Date: Tue, 28 Apr 2026 16:06:13 +0200 Subject: [PATCH] Changed self: Self to self: *const SElf in tensor for performance --- src/Base.zig | 46 +++++------ src/Tensor.zig | 211 +++++++++++++++++++++++-------------------------- 2 files changed, 123 insertions(+), 134 deletions(-) diff --git a/src/Base.zig b/src/Base.zig index eeaae49..9002204 100644 --- a/src/Base.zig +++ b/src/Base.zig @@ -11,7 +11,7 @@ fn PhysicalConstant(comptime d: Dimensions.ArgOpts, comptime val: f64, comptime pub const scales = Scales.init(s); /// Instantiates the constant into a specific numeric type. - pub fn Of(comptime T: type) *const Tensor(T, d, s, &.{1}) { + pub fn Of(comptime T: type) Tensor(T, d, s, &.{1}) { const casted_val: T = switch (@typeInfo(T)) { .float => @floatCast(val), .int => @intFromFloat(val), @@ -175,8 +175,8 @@ test "BaseQuantities - Core dimensions instantiation" { const Kmh = Speed.Scaled(f32, .{ .L = .k, .T = .hour }); const speed = Kmh.splat(120); try std.testing.expectEqual(120.0, speed.data[0]); - try std.testing.expectEqual(.k, @TypeOf(speed.*).scales.get(.L)); - try std.testing.expectEqual(.hour, @TypeOf(speed.*).scales.get(.T)); + try std.testing.expectEqual(.k, @TypeOf(speed).scales.get(.L)); + try std.testing.expectEqual(.hour, @TypeOf(speed).scales.get(.T)); } test "BaseQuantities - Kinematics equations" { @@ -186,12 +186,12 @@ test "BaseQuantities - Kinematics equations" { // Velocity = Distance / Time const v = d.div(t); try std.testing.expectEqual(25.0, v.data[0]); - try comptime std.testing.expect(Speed.dims.eql(@TypeOf(v.*).dims)); + try comptime std.testing.expect(Speed.dims.eql(@TypeOf(v).dims)); // Acceleration = Velocity / Time const a = v.div(t); try std.testing.expectEqual(12.5, a.data[0]); - try comptime std.testing.expect(Acceleration.dims.eql(@TypeOf(a.*).dims)); + try comptime std.testing.expect(Acceleration.dims.eql(@TypeOf(a).dims)); } test "BaseQuantities - Dynamics (Force and Work)" { @@ -203,13 +203,13 @@ test "BaseQuantities - Dynamics (Force and Work)" { // Force = mass * acceleration const f = m.mul(a); try std.testing.expectEqual(98, f.data[0]); - try comptime std.testing.expect(Force.dims.eql(@TypeOf(f.*).dims)); + try comptime std.testing.expect(Force.dims.eql(@TypeOf(f).dims)); // Energy (Work) = Force * distance const distance = Meter.Of(f32).splat(5.0); const energy = f.mul(distance); try std.testing.expectEqual(490, energy.data[0]); - try comptime std.testing.expect(Energy.dims.eql(@TypeOf(energy.*).dims)); + try comptime std.testing.expect(Energy.dims.eql(@TypeOf(energy).dims)); } test "BaseQuantities - Electric combinations" { @@ -219,42 +219,42 @@ test "BaseQuantities - Electric combinations" { // Charge = Current * time const charge = current.mul(time); try std.testing.expectEqual(6.0, charge.data[0]); - try comptime std.testing.expect(ElectricCharge.dims.eql(@TypeOf(charge.*).dims)); + try comptime std.testing.expect(ElectricCharge.dims.eql(@TypeOf(charge).dims)); } test "Constants - Initialization and dimension checks" { // Speed of Light const c = Constants.SpeedOfLight.Of(f64); try std.testing.expectEqual(299792458.0, c.data[0]); - try std.testing.expectEqual(1, @TypeOf(c.*).dims.get(.L)); - try std.testing.expectEqual(-1, @TypeOf(c.*).dims.get(.T)); + try std.testing.expectEqual(1, @TypeOf(c).dims.get(.L)); + try std.testing.expectEqual(-1, @TypeOf(c).dims.get(.T)); // Electron Mass (verifying scale as well) const me = Constants.ElectronMass.Of(f64); try std.testing.expectEqual(9.1093837139e-31, me.data[0]); - try std.testing.expectEqual(1, @TypeOf(me.*).dims.get(.M)); - try std.testing.expectEqual(.k, @TypeOf(me.*).scales.get(.M)); // Should be scaled to kg + try std.testing.expectEqual(1, @TypeOf(me).dims.get(.M)); + try std.testing.expectEqual(.k, @TypeOf(me).scales.get(.M)); // Should be scaled to kg // Boltzmann Constant (Complex derived dimensions) const kb = Constants.Boltzmann.Of(f64); try std.testing.expectEqual(1.380649e-23, kb.data[0]); - try std.testing.expectEqual(1, @TypeOf(kb.*).dims.get(.M)); - try std.testing.expectEqual(2, @TypeOf(kb.*).dims.get(.L)); - try std.testing.expectEqual(-2, @TypeOf(kb.*).dims.get(.T)); - try std.testing.expectEqual(-1, @TypeOf(kb.*).dims.get(.Tp)); - try std.testing.expectEqual(.k, @TypeOf(kb.*).scales.get(.M)); + try std.testing.expectEqual(1, @TypeOf(kb).dims.get(.M)); + try std.testing.expectEqual(2, @TypeOf(kb).dims.get(.L)); + try std.testing.expectEqual(-2, @TypeOf(kb).dims.get(.T)); + try std.testing.expectEqual(-1, @TypeOf(kb).dims.get(.Tp)); + try std.testing.expectEqual(.k, @TypeOf(kb).scales.get(.M)); // Vacuum Permittivity const eps0 = Constants.VacuumPermittivity.Of(f64); try std.testing.expectEqual(8.8541878188e-12, eps0.data[0]); - try std.testing.expectEqual(-1, @TypeOf(eps0.*).dims.get(.M)); - try std.testing.expectEqual(-3, @TypeOf(eps0.*).dims.get(.L)); - try std.testing.expectEqual(4, @TypeOf(eps0.*).dims.get(.T)); - try std.testing.expectEqual(2, @TypeOf(eps0.*).dims.get(.I)); + try std.testing.expectEqual(-1, @TypeOf(eps0).dims.get(.M)); + try std.testing.expectEqual(-3, @TypeOf(eps0).dims.get(.L)); + try std.testing.expectEqual(4, @TypeOf(eps0).dims.get(.T)); + try std.testing.expectEqual(2, @TypeOf(eps0).dims.get(.I)); // Fine Structure Constant (Dimensionless) const alpha = Constants.FineStructure.Of(f64); try std.testing.expectEqual(0.0072973525643, alpha.data[0]); - try std.testing.expectEqual(0, @TypeOf(alpha.*).dims.get(.M)); - try std.testing.expectEqual(0, @TypeOf(alpha.*).dims.get(.L)); + try std.testing.expectEqual(0, @TypeOf(alpha).dims.get(.M)); + try std.testing.expectEqual(0, @TypeOf(alpha).dims.get(.L)); } diff --git a/src/Tensor.zig b/src/Tensor.zig index 30d1de5..7619c03 100644 --- a/src/Tensor.zig +++ b/src/Tensor.zig @@ -139,10 +139,10 @@ inline fn RhsTensorType(comptime T: type, comptime Rhs: type) type { /// Take the anyvalue coming from operation and if it is a Tensor, return it. /// If it is a float or int, return a Tensor(T, .{}, .{}, .{1}).splat(r). -inline fn toRhsTensor(comptime T: type, r: anytype) RhsTensorType(T, if (@typeInfo(@TypeOf(r)) == .pointer) @TypeOf(r.*) else @TypeOf(r)) { +inline fn toRhsTensor(comptime T: type, r: anytype) RhsTensorType(T, @TypeOf(r)) { const is_ptr = @typeInfo(@TypeOf(r)) == .pointer; - const Rhs = if (is_ptr) @TypeOf(r.*) else @TypeOf(r); - if (comptime isTensor(Rhs)) return r; + const Rhs = @TypeOf(if (is_ptr) r.* else r); + if (comptime isTensor(Rhs)) return if (is_ptr) r.* else r; const scalar: T = switch (@typeInfo(Rhs)) { .comptime_int => switch (comptime @typeInfo(T)) { .float => @as(T, @floatFromInt(r)), @@ -238,12 +238,12 @@ pub fn Tensor( } /// Broadcast a single value across all elements. - pub inline fn splat(v: T) *const Self { - return &.{ .data = @splat(v) }; + pub inline fn splat(v: T) Self { + return .{ .data = @splat(v) }; } - pub const zero: Self = splat(0).*; - pub const one: Self = splat(1).*; + pub const zero: Self = splat(0); + pub const one: Self = splat(1); /// Return a mutable slice to the flat storage — zero-copy WebGPU buffer mapping. pub inline fn asSlice(self: *Self) []T { @@ -253,12 +253,11 @@ pub fn Tensor( inline fn RhsT(comptime Rhs: type) type { return RhsTensorType(T, Rhs); } - - inline fn rhs(r: anytype) RhsT(if (@typeInfo(@TypeOf(r)) == .pointer) @TypeOf(r.*) else @TypeOf(r)) { + inline fn rhs(r: anytype) RhsT(@TypeOf(r)) { return toRhsTensor(T, r); } - inline fn broadcastToVec(comptime RhsType: type, r: *const RhsType) Vec { + inline fn broadcastToVec(comptime RhsType: type, r: RhsType) Vec { return if (comptime RhsType.total == 1 and total > 1) @splat(r.data[0]) else @@ -270,11 +269,11 @@ pub fn Tensor( pub inline fn add(self: *const Self, r: anytype) Tensor( T, dims.argsOpt(), - finerScales(Self, RhsT(if (@typeInfo(@TypeOf(r)) == .pointer) @TypeOf(r.*) else @TypeOf(r))).argsOpt(), + finerScales(Self, RhsT(@TypeOf(r))).argsOpt(), shape_, ) { const rhs_t = rhs(r); - const RhsType = @TypeOf(rhs_t.*); + const RhsType = @TypeOf(rhs_t); if (comptime !dims.eql(RhsType.dims)) @compileError("Dimension mismatch in add: " ++ dims.str() ++ " vs " ++ RhsType.dims.str()); if (comptime RhsType.total != 1 and !shapeEql(shape_, RhsType.shape)) @@ -297,11 +296,11 @@ pub fn Tensor( pub inline fn sub(self: *const Self, r: anytype) Tensor( T, dims.argsOpt(), - finerScales(Self, RhsT(if (@typeInfo(@TypeOf(r)) == .pointer) @TypeOf(r.*) else @TypeOf(r))).argsOpt(), + finerScales(Self, RhsT(@TypeOf(r))).argsOpt(), shape_, ) { const rhs_t = rhs(r); - const RhsType = @TypeOf(rhs_t.*); + const RhsType = @TypeOf(rhs_t); if (comptime !dims.eql(RhsType.dims)) @compileError("Dimension mismatch in sub: " ++ dims.str() ++ " vs " ++ RhsType.dims.str()); if (comptime RhsType.total != 1 and !shapeEql(shape_, RhsType.shape)) @@ -323,12 +322,12 @@ pub fn Tensor( /// Shape {1} RHS is automatically broadcast across all elements. pub inline fn mul(self: *const Self, r: anytype) Tensor( T, - dims.add(RhsT(if (@typeInfo(@TypeOf(r)) == .pointer) @TypeOf(r.*) else @TypeOf(r)).dims).argsOpt(), - finerScales(Self, RhsT(if (@typeInfo(@TypeOf(r)) == .pointer) @TypeOf(r.*) else @TypeOf(r))).argsOpt(), + dims.add(RhsT(@TypeOf(r)).dims).argsOpt(), + finerScales(Self, RhsT(@TypeOf(r))).argsOpt(), shape_, ) { const rhs_q = rhs(r); - const RhsType = @TypeOf(rhs_q.*); + const RhsType = @TypeOf(rhs_q); if (comptime RhsType.total != 1 and !shapeEql(shape_, RhsType.shape)) @compileError("Shape mismatch in mul: element-wise operations require identical shapes, or a scalar RHS."); @@ -344,12 +343,12 @@ pub fn Tensor( /// Shape {1} RHS is automatically broadcast across all elements. pub inline fn div(self: *const Self, r: anytype) Tensor( T, - dims.sub(RhsT(if (@typeInfo(@TypeOf(r)) == .pointer) @TypeOf(r.*) else @TypeOf(r)).dims).argsOpt(), - finerScales(Self, RhsT(if (@typeInfo(@TypeOf(r)) == .pointer) @TypeOf(r.*) else @TypeOf(r))).argsOpt(), + dims.sub(RhsT(@TypeOf(r)).dims).argsOpt(), + finerScales(Self, RhsT(@TypeOf(r))).argsOpt(), shape_, ) { const rhs_q = rhs(r); - const RhsType = @TypeOf(rhs_q.*); + const RhsType = @TypeOf(rhs_q); if (comptime RhsType.total != 1 and !shapeEql(shape_, RhsType.shape)) @compileError("Shape mismatch in div: element-wise operations require identical shapes, or a scalar RHS."); @@ -371,13 +370,13 @@ pub fn Tensor( } /// Raise every element to a comptime integer exponent. - pub inline fn pow(self: Self, comptime exp: comptime_int) Tensor( + pub inline fn pow(self: *const Self, comptime exp: comptime_int) Tensor( T, dims.scale(exp).argsOpt(), scales.argsOpt(), shape_, ) { - if (comptime exp == 0) return &.{ .data = @splat(1) }; + if (comptime exp == 0) return .{ .data = @splat(1) }; if (comptime exp == 1) return self; var base = self.data; @@ -401,7 +400,7 @@ pub fn Tensor( } /// Square root of every element. All dimension exponents must be even. - pub inline fn sqrt(self: Self) Tensor( + pub inline fn sqrt(self: *const Self) Tensor( T, dims.div(2).argsOpt(), scales.argsOpt(), @@ -424,7 +423,7 @@ pub fn Tensor( } /// Negate every element. - pub inline fn negate(self: Self) Self { + pub inline fn negate(self: *const Self) Self { return .{ .data = -self.data }; } @@ -451,14 +450,14 @@ pub fn Tensor( const DestVec = @Vector(total, DestT); if (comptime ratio == 1.0 and T == DestT) - return &.{ .data = self.data }; + return .{ .data = self.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 &.{ + return .{ .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) @@ -474,22 +473,22 @@ pub fn Tensor( if (comptime T == DestT) { if (comptime @typeInfo(T) == .float) - return &.{ .data = self.data * @as(DestVec, @splat(@as(T, @floatCast(ratio)))) }; + return .{ .data = self.data * @as(DestVec, @splat(@as(T, @floatCast(ratio)))) }; if (comptime ratio >= 1.0) { const mult: T = comptime @intFromFloat(@round(ratio)); - return &.{ .data = self.data *| @as(Vec, @splat(mult)) }; + return .{ .data = self.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(self.data + @as(Vec, @splat(half)), @as(Vec, @splat(div_val))) }; + return .{ .data = @divTrunc(self.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 offsets = @select(T, is_pos, @as(Vec, @splat(half)), @as(Vec, @splat(-half))); - return &.{ .data = @divTrunc(self.data + offsets, @as(Vec, @splat(div_val))) }; + return .{ .data = @divTrunc(self.data + offsets, @as(Vec, @splat(div_val))) }; } } } @@ -505,8 +504,8 @@ pub fn Tensor( const scaled = float_vec * @as(FVec, @splat(ratio)); return switch (comptime @typeInfo(DestT)) { - .float => &.{ .data = @floatCast(scaled) }, - .int => &.{ .data = @intFromFloat(@round(scaled)) }, + .float => .{ .data = @floatCast(scaled) }, + .int => .{ .data = @intFromFloat(@round(scaled)) }, else => unreachable, }; } @@ -519,7 +518,7 @@ pub fn Tensor( /// Resolve both sides to the finer scale, broadcasting shape {1} RHS if needed. inline fn resolveScalePair(self: *const Self, rhs_q: anytype) struct { l: Vec, r: Vec } { - const RhsType = @TypeOf(rhs_q.*); + const RhsType = @TypeOf(rhs_q); if (comptime RhsType.total != 1 and !shapeEql(shape_, RhsType.shape)) @compileError("Shape mismatch in comparison: element-wise operations require identical shapes, or a scalar RHS."); @@ -535,7 +534,7 @@ pub fn Tensor( pub inline fn eq(self: *const Self, r: anytype) CmpResult { const rhs_q = rhs(r); - if (comptime !dims.eql(@TypeOf(rhs_q.*).dims)) + if (comptime !dims.eql(@TypeOf(rhs_q).dims)) @compileError("Dimension mismatch in ne."); const p = resolveScalePair(self, rhs_q); return cmpResult(p.l == p.r); @@ -543,7 +542,7 @@ pub fn Tensor( pub inline fn ne(self: *const Self, r: anytype) CmpResult { const rhs_q = rhs(r); - if (comptime !dims.eql(@TypeOf(rhs_q.*).dims)) + if (comptime !dims.eql(@TypeOf(rhs_q).dims)) @compileError("Dimension mismatch in ne."); const p = resolveScalePair(self, rhs_q); return cmpResult(p.l != p.r); @@ -551,7 +550,7 @@ pub fn Tensor( pub inline fn gt(self: *const Self, r: anytype) CmpResult { const rhs_q = rhs(r); - if (comptime !dims.eql(@TypeOf(rhs_q.*).dims)) + if (comptime !dims.eql(@TypeOf(rhs_q).dims)) @compileError("Dimension mismatch in gt."); const p = resolveScalePair(self, rhs_q); return cmpResult(p.l > p.r); @@ -559,7 +558,7 @@ pub fn Tensor( pub inline fn gte(self: *const Self, r: anytype) CmpResult { const rhs_q = rhs(r); - if (comptime !dims.eql(@TypeOf(rhs_q.*).dims)) + if (comptime !dims.eql(@TypeOf(rhs_q).dims)) @compileError("Dimension mismatch in gte."); const p = resolveScalePair(self, rhs_q); return cmpResult(p.l >= p.r); @@ -567,7 +566,7 @@ pub fn Tensor( pub inline fn lt(self: *const Self, r: anytype) CmpResult { const rhs_q = rhs(r); - if (comptime !dims.eql(@TypeOf(rhs_q.*).dims)) + if (comptime !dims.eql(@TypeOf(rhs_q).dims)) @compileError("Dimension mismatch in lt."); const p = resolveScalePair(self, rhs_q); return cmpResult(p.l < p.r); @@ -575,7 +574,7 @@ pub fn Tensor( pub inline fn lte(self: *const Self, r: anytype) CmpResult { const rhs_q = rhs(r); - if (comptime !dims.eql(@TypeOf(rhs_q.*).dims)) + if (comptime !dims.eql(@TypeOf(rhs_q).dims)) @compileError("Dimension mismatch in lte."); const p = resolveScalePair(self, rhs_q); return cmpResult(p.l <= p.r); @@ -583,7 +582,7 @@ pub fn Tensor( /// True iff every element is equal after scale resolution. pub inline fn eqAll(self: *const Self, other: anytype) bool { - if (comptime !dims.eql(@TypeOf(other.*).dims)) + if (comptime !dims.eql(@TypeOf(other).dims)) @compileError("Dimension mismatch in eqAll."); const p = resolveScalePair(self, other); return @reduce(.And, p.l == p.r); @@ -600,7 +599,7 @@ pub fn Tensor( comptime axis_a: usize, comptime axis_b: usize, ) blk: { - const OT = @TypeOf(other.*); + const OT = @TypeOf(other); if (axis_a >= rank) @compileError("contract: axis_a out of bounds"); if (axis_b >= OT.rank) @compileError("contract: axis_b out of bounds"); if (shape_[axis_a] != OT.shape[axis_b]) @compileError("contract: shape mismatch at contraction axes"); @@ -609,14 +608,14 @@ pub fn Tensor( const sb = shapeRemoveAxis(OT.shape, axis_b); const rs_raw = shapeCat(&sa, &sb); const rs: []const comptime_int = if (rs_raw.len == 0) &.{1} else &rs_raw; - break :blk *const Tensor( + break :blk Tensor( T, dims.add(OT.dims).argsOpt(), finerScales(Self, OT).argsOpt(), rs, ); } { - const OT = @TypeOf(other.*); + const OT = @TypeOf(other); const k: usize = comptime shape_[axis_a]; // contraction dimension const sa = comptime shapeRemoveAxis(shape_, axis_a); @@ -640,14 +639,14 @@ pub fn Tensor( // FAST PATH: Dot Product if (comptime rank == 1 and OT.rank == 1 and axis_a == 0 and axis_b == 0) { if (comptime !isInt(T)) { - return &.{ .data = @splat(@reduce(.Add, a_data * b_data)) }; + return .{ .data = @splat(@reduce(.Add, a_data * b_data)) }; } else { // For integers, we do a vectorized saturating multiply, // then convert to an array to do a saturating sum const mul_arr: [total]T = a_data *| b_data; var acc: T = 0; for (mul_arr) |val| acc +|= val; - return &.{ .data = @splat(acc) }; + return .{ .data = @splat(acc) }; } } @@ -679,7 +678,7 @@ pub fn Tensor( } } // Return the initialized Tensor struct - return &.{ .data = res_arr }; + return .{ .data = res_arr }; } // FALLBACK PATH @@ -711,12 +710,12 @@ pub fn Tensor( } // Return the initialized Tensor struct - return &.{ .data = result_arr }; + return .{ .data = result_arr }; } /// 3D Cross Product. Only defined for Rank-1 tensors of length 3. /// Result dimensions are the sum of input dimensions. - pub inline fn cross(self: *const Self, other: anytype) *const Tensor( + pub inline fn cross(self: *const Self, other: anytype) Tensor( T, dims.add(RhsT(@TypeOf(other)).dims).argsOpt(), finerScales(Self, RhsT(@TypeOf(other))).argsOpt(), @@ -745,7 +744,7 @@ pub fn Tensor( res[2] = (l[0] * r[1]) - (l[1] * r[0]); } - return &.{ .data = res }; + return .{ .data = res }; } /// Sum of squared elements. Cheaper than length(); use for ordering. @@ -764,17 +763,17 @@ pub fn Tensor( } /// Product of all elements. Result has shape {1}; dimension exponent * total. - pub inline fn product(self: *const Self) *const Tensor( + pub inline fn product(self: *const Self) Tensor( T, dims.scale(@as(comptime_int, total)).argsOpt(), scales.argsOpt(), &.{1}, ) { - return &.{ .data = .{@reduce(.Mul, self.data)} }; + return .{ .data = .{@reduce(.Mul, self.data)} }; } pub fn formatNumber( - self: Self, + self: *const Self, writer: *std.Io.Writer, options: std.fmt.Number, ) !void { @@ -878,7 +877,7 @@ test "Scalar Add" { 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)); + try std.testing.expectEqual(1, @TypeOf(added).dims.get(.L)); const distance3 = KiloMeter.splat(2); const added2 = distance.add(distance3); @@ -917,13 +916,13 @@ test "Scalar MulBy" { const t = Second.splat(4); const at = d.mul(t); try std.testing.expectEqual(12, at.data[0]); - try std.testing.expectEqual(1, @TypeOf(at.*).dims.get(.L)); - try std.testing.expectEqual(1, @TypeOf(at.*).dims.get(.T)); + try std.testing.expectEqual(1, @TypeOf(at).dims.get(.L)); + try std.testing.expectEqual(1, @TypeOf(at).dims.get(.T)); const d2 = Meter.splat(5); const area = d.mul(d2); try std.testing.expectEqual(15, area.data[0]); - 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" { @@ -933,8 +932,8 @@ test "Scalar MulBy with scale" { const dist = KiloMeter.splat(2.0); const mass = KiloGram.splat(3.0); 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)); + try std.testing.expectEqual(1, @TypeOf(prod).dims.get(.L)); + try std.testing.expectEqual(1, @TypeOf(prod).dims.get(.M)); } test "Scalar MulBy with type change" { @@ -945,10 +944,9 @@ test "Scalar MulBy with type change" { const d = Meter.splat(3); const t = Second.splat(4); - const ms = d.mul(t); - try std.testing.expectEqual(12, ms.to(KmSec).data[0]); - try std.testing.expectApproxEqAbs(12.0, ms.to(KmSec_f).data[0], 0.0001); + try std.testing.expectEqual(12, d.mul(t).to(KmSec).data[0]); + try std.testing.expectApproxEqAbs(12.0, d.mul(t).to(KmSec_f).data[0], 0.0001); } test "Scalar MulBy small" { @@ -974,7 +972,7 @@ test "Scalar Sqrt" { var d = MeterSquare.splat(9); var scaled = d.sqrt(); try std.testing.expectEqual(3, scaled.data[0]); - try std.testing.expectEqual(1, @TypeOf(scaled.*).dims.get(.L)); + try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L)); d = MeterSquare.splat(-5); scaled = d.sqrt(); @@ -1017,7 +1015,7 @@ test "Scalar Finer scales skip dim 0" { const km = KiloMetre.splat(4); const vel = r.mul(km); try std.testing.expectEqual(120, vel.data[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" { @@ -1052,10 +1050,10 @@ test "Scalar Format" { const accel = MeterPerSecondSq.splat(9.81); var buf: [64]u8 = undefined; - var res = try std.fmt.bufPrint(&buf, "{d:.2}", .{m.*}); + var res = try std.fmt.bufPrint(&buf, "{d:.2}", .{m}); try std.testing.expectEqualStrings("1.23m", res); - res = try std.fmt.bufPrint(&buf, "{d}", .{accel.*}); + res = try std.fmt.bufPrint(&buf, "{d}", .{accel}); try std.testing.expectEqualStrings("9.81m.ns⁻²", res); } @@ -1125,13 +1123,13 @@ test "Vector format" { 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 } }; + const momentum = KgMeterPerSecond{ .data = .{ 43, 0, 11 } }; var buf: [64]u8 = undefined; - var res = try std.fmt.bufPrint(&buf, "{d}", .{accel.*}); + 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.*}); + res = try std.fmt.bufPrint(&buf, "{d:.2}", .{momentum}); try std.testing.expectEqualStrings("(43.00, 0.00, 11.00)m.kg.s⁻¹", res); } @@ -1148,8 +1146,8 @@ test "Vector Vec3 Init and Basic Arithmetic" { 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 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]); @@ -1178,24 +1176,24 @@ test "Vector Kinematics (scalar mul/div broadcast)" { 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)); + 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)); + try std.testing.expectEqual(0, @TypeOf(new_pos).dims.get(.T)); } test "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 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)); + try std.testing.expectEqual(0, @TypeOf(dv).dims.get(.L)); } test "Vector Conversions" { @@ -1207,14 +1205,14 @@ test "Vector Conversions" { 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)); + try std.testing.expectEqual(UnitScale.none, @TypeOf(v_m).scales.get(.L)); } test "Vector Length" { const MeterInt3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3}); const MeterFloat3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3}); - const v_int = &MeterInt3{ .data = .{ 3, 4, 0 } }; + const v_int = MeterInt3{ .data = .{ 3, 4, 0 } }; try std.testing.expectEqual(25, v_int.lengthSqr()); try std.testing.expectEqual(5, v_int.length()); @@ -1227,9 +1225,9 @@ test "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 } }; + 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)); @@ -1252,7 +1250,7 @@ test "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 positions = Meter3{ .data = .{ 500.0, 1200.0, 3000.0 } }; const threshold = KiloMeter1.splat(1); // 1 km = 1000 m const exceeded = positions.gt(threshold); @@ -1270,22 +1268,22 @@ test "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 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)); + 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 "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 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 })]); @@ -1297,59 +1295,59 @@ test "Vector contract — matrix multiply (rank-2 * rank-2)" { test "Vector Abs, Pow, Sqrt and Product" { const Meter3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3}); - const v1 = &Meter3{ .data = .{ -2.0, 3.0, -4.0 } }; + 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)); + 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)); + 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)); + try std.testing.expectEqual(1, @TypeOf(sqrted).dims.get(.L)); } test "Vector mul comptime_int broadcast" { const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3}); - const v = &Meter3{ .data = .{ 1, 2, 3 } }; + const v = Meter3{ .data = .{ 1, 2, 3 } }; const scaled = v.mul(10); try std.testing.expectEqual(10, scaled.data[0]); try std.testing.expectEqual(20, scaled.data[1]); try std.testing.expectEqual(30, scaled.data[2]); - try std.testing.expectEqual(1, @TypeOf(scaled.*).dims.get(.L)); + try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L)); } test "Vector mul comptime_float broadcast" { const MeterF3 = Tensor(f32, .{ .L = 1 }, .{}, &.{3}); - const v = &MeterF3{ .data = .{ 1.0, 2.0, 4.0 } }; + const v = MeterF3{ .data = .{ 1.0, 2.0, 4.0 } }; const scaled = v.mul(0.5); try std.testing.expectApproxEqAbs(0.5, scaled.data[0], 1e-6); try std.testing.expectApproxEqAbs(1.0, scaled.data[1], 1e-6); try std.testing.expectApproxEqAbs(2.0, scaled.data[2], 1e-6); - try std.testing.expectEqual(1, @TypeOf(scaled.*).dims.get(.L)); + try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L)); } test "Vector div comptime_int broadcast" { const Meter3 = Tensor(i32, .{ .L = 1 }, .{}, &.{3}); - const v = &Meter3{ .data = .{ 10, 20, 30 } }; + const v = Meter3{ .data = .{ 10, 20, 30 } }; const halved = v.div(2); try std.testing.expectEqual(5, halved.data[0]); try std.testing.expectEqual(10, halved.data[1]); try std.testing.expectEqual(15, halved.data[2]); - try std.testing.expectEqual(1, @TypeOf(halved.*).dims.get(.L)); + try std.testing.expectEqual(1, @TypeOf(halved).dims.get(.L)); } test "Vector div comptime_float broadcast" { const MeterF3 = Tensor(f64, .{ .L = 1 }, .{}, &.{3}); - const v = &MeterF3{ .data = .{ 9.0, 6.0, 3.0 } }; + const v = MeterF3{ .data = .{ 9.0, 6.0, 3.0 } }; const r = v.div(3.0); try std.testing.expectApproxEqAbs(3.0, r.data[0], 1e-9); try std.testing.expectApproxEqAbs(2.0, r.data[1], 1e-9); @@ -1358,7 +1356,7 @@ test "Vector div comptime_float broadcast" { test "Vector eq broadcast on dimensionless" { const DimLess3 = Tensor(i32, .{}, .{}, &.{3}); - const v = &DimLess3{ .data = .{ 1, 2, 3 } }; + const v = DimLess3{ .data = .{ 1, 2, 3 } }; const eq_res = v.eq(2); try std.testing.expectEqual(false, eq_res[0]); @@ -1373,7 +1371,7 @@ test "Vector eq broadcast on dimensionless" { test "Tensor idx helper and matrix access" { const Mat3x3 = Tensor(f32, .{}, .{}, &.{ 3, 3 }); - var m = Mat3x3.zero; + 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; @@ -1396,12 +1394,3 @@ test "Tensor strides_arr correctness" { try std.testing.expectEqual(4, T3.strides_arr[1]); try std.testing.expectEqual(1, T3.strides_arr[2]); } - -test "Big Tensor" { - const Tens = Tensor(f32, .{}, .{}, &.{1_000_000}); - const t1 = Tens.splat(2); - const t2 = Tens.splat(3); - const t3 = t1.add(t2); - - try std.testing.expectApproxEqAbs(5, t3.data[0], 0.0001); -}