diff --git a/build.zig.zon b/build.zig.zon index b148b26903..11fda9ad8c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .trinity, - .version = "0.15.2", + .version = "0.16.0", .dependencies = .{ .emsdk = .{ .url = "git+https://github.com/emscripten-core/emsdk?ref=4.0.9#3bcf1dcd01f040f370e10fe673a092d9ed79ebb5", @@ -10,14 +10,6 @@ .url = "git+https://github.com/raylib-zig/raylib-zig#cd71c85d571027ac8033357f83b124ee051825b3", .hash = "raylib_zig-5.6.0-dev-KE8REENOBQC-m5nK7M2b5aKSIubJPbPLUYcRhT7aT3RN", }, - .zodd = .{ - .url = "git+https://github.com/CogitatorTech/zodd#main", - .hash = "zodd-0.1.0-alpha.3-TJEk3Y7uAQDOkBXaPV_lynH1rF-eDwf9PnVc13MpPFym", - }, - .zig_hdc = .{ - .url = "git+https://github.com/gHashTag/zig-hdc#main", - .hash = "zig-hdc-0.1.0-????????????????????????????????????????", - }, }, .paths = .{""}, .fingerprint = 0xa9f69745ba7ed82d, diff --git a/build_minimal.zig b/build_minimal.zig index 470e37c960..af267c1c7a 100644 --- a/build_minimal.zig +++ b/build_minimal.zig @@ -1,16 +1,18 @@ const std = @import("std"); -pub fn build(b: *std.Build) !void { +pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const target = b.standardTargetOptions(.{}); - // Minimal working build - just HSLM CLI first - const hslm_cli = b.addExecutable(.{ - .name = "hslm-cli", + const root_mod = b.createModule(.{ .root_source_file = b.path("src/hslm/cli.zig"), .target = target, .optimize = optimize, }); + const hslm_cli = b.addExecutable(.{ + .name = "hslm-cli", + .root_module = root_mod, + }); b.installArtifact(hslm_cli); const hslm_run = b.addRunArtifact(hslm_cli); diff --git a/src/brain/angular_gyrus.zig b/src/brain/angular_gyrus.zig new file mode 100644 index 0000000000..142fad0277 --- /dev/null +++ b/src/brain/angular_gyrus.zig @@ -0,0 +1,107 @@ +const std = @import("std"); + +pub const PHI: f64 = 1.6180339887498948482; +pub const PHI_INV: f64 = 1.0 / PHI; +pub const PHI_SQ: f64 = PHI * PHI; +pub const PHI_INV_SQ: f64 = 1.0 / PHI_SQ; +pub const TRINITY: f64 = PHI_SQ + PHI_INV_SQ; + +pub const FormatInfo = struct { + name: []const u8, + total_bits: u8, + exp_bits: u8, + mant_bits: u8, + exp_mant_ratio: f64, + phi_distance: f64, + golden_pct: f64, +}; + +pub const GF16_INFO = FormatInfo{ + .name = "GF16", + .total_bits = 16, + .exp_bits = 6, + .mant_bits = 9, + .exp_mant_ratio = 6.0 / 9.0, + .phi_distance = @abs(6.0 / 9.0 - PHI_INV), + .golden_pct = (1.0 - @abs(6.0 / 9.0 - PHI_INV) / PHI_INV) * 100.0, +}; + +pub const BF16_INFO = FormatInfo{ + .name = "BF16", + .total_bits = 16, + .exp_bits = 8, + .mant_bits = 7, + .exp_mant_ratio = 8.0 / 7.0, + .phi_distance = @abs(8.0 / 7.0 - PHI_INV), + .golden_pct = (1.0 - @abs(8.0 / 7.0 - PHI_INV) / PHI_INV) * 100.0, +}; + +pub const FP16_INFO = FormatInfo{ + .name = "FP16", + .total_bits = 16, + .exp_bits = 5, + .mant_bits = 10, + .exp_mant_ratio = 5.0 / 10.0, + .phi_distance = @abs(5.0 / 10.0 - PHI_INV), + .golden_pct = (1.0 - @abs(5.0 / 10.0 - PHI_INV) / PHI_INV) * 100.0, +}; + +pub fn phiDistance(exp_bits: u8, mant_bits: u8) f64 { + return @abs(@as(f64, @floatFromInt(exp_bits)) / @as(f64, @floatFromInt(mant_bits)) - PHI_INV); +} + +pub fn goldenPct(exp_bits: u8, mant_bits: u8) f64 { + const dist = phiDistance(exp_bits, mant_bits); + return (1.0 - dist / PHI_INV) * 100.0; +} + +pub fn formatTable(writer: anytype) !void { + try writer.print("\n Format Comparison (phi-distance from 1/phi = {d:.6})\n", .{PHI_INV}); + try writer.print(" {s}\n", .{"-" * 72}); + try writer.print(" {s:<8} {s:>6} {s:>6} {s:>6} {s:>10} {s:>10}\n", .{ "Format", "Bits", "Exp", "Mant", "e/m ratio", "% golden" }); + try writer.print(" {s}\n", .{"-" * 72}); + + const formats = [_]FormatInfo{ GF16_INFO, BF16_INFO, FP16_INFO }; + for (&formats) |f| { + try writer.print(" {s:<8} {d:>6} {d:>6} {d:>6} {d:>10.4} {d:>9.1f}%\n", .{ + f.name, f.total_bits, f.exp_bits, f.mant_bits, f.exp_mant_ratio, f.golden_pct, + }); + } + try writer.print(" {s}\n\n", .{"-" * 72}); +} + +pub fn autoSelectFormat(layer_idx: usize, total_layers: usize) FormatInfo { + const ratio = @as(f64, @floatFromInt(layer_idx)) / @as(f64, @floatFromInt(total_layers)); + if (ratio < 0.2) return FP16_INFO; + if (ratio < 0.8) return GF16_INFO; + return BF16_INFO; +} + +test "phi distance GF16" { + const dist = phiDistance(6, 9); + try std.testing.expect(dist < 0.05); + try std.testing.expect(dist > 0); +} + +test "phi distance GF16 is closest" { + const gf16_dist = phiDistance(6, 9); + const bf16_dist = phiDistance(8, 7); + const fp16_dist = phiDistance(5, 10); + try std.testing.expect(gf16_dist < bf16_dist); + try std.testing.expect(gf16_dist < fp16_dist); +} + +test "golden pct GF16" { + const pct = goldenPct(6, 9); + try std.testing.expect(pct > 90.0); +} + +test "auto select format" { + try std.testing.expectEqualStrings("FP16", autoSelectFormat(0, 10).name); + try std.testing.expectEqualStrings("GF16", autoSelectFormat(5, 10).name); + try std.testing.expectEqualStrings("BF16", autoSelectFormat(9, 10).name); +} + +test "trinity identity" { + try std.testing.expectApproxEqAbs(@as(f64, 3.0), TRINITY, 1e-10); +} diff --git a/src/brain/fusiform_gyrus.zig b/src/brain/fusiform_gyrus.zig new file mode 100644 index 0000000000..2094fe1f2b --- /dev/null +++ b/src/brain/fusiform_gyrus.zig @@ -0,0 +1,64 @@ +const std = @import("std"); + +pub fn encodeF32ToGF16(v: f32) u16 { + if (v == 0.0) return 0; + if (!std.math.isFinite(v)) return if (v < 0) 0xFFFF else 0x7FFF; + + const sign: u16 = if (v < 0) 1 << 15 else 0; + const abs_v = @abs(v); + + var exp: i16 = 0; + var mant_f: f64 = abs_v; + while (mant_f >= 1.0 and exp < 31) : (exp += 1) mant_f /= 2.0; + while (mant_f < 0.5 and exp > -32) : (exp -= 1) mant_f *= 2.0; + + const exp_u6: u16 = @intCast(std.math.clamp(@as(i16, 31) + exp, 0, 63)); + const mant_u9: u16 = @intFromFloat(std.math.clamp((mant_f - 0.5) * 512.0, 0, 511)); + + return sign | (exp_u6 << 9) | (mant_u9 & 0x1FF); +} + +pub fn decodeGF16ToF32(raw: u16) f32 { + if (raw == 0) return 0.0; + if (raw == 0x8000) return -0.0; + + const sign: f32 = if ((raw >> 15) & 1 == 1) -1.0 else 1.0; + const exp: u16 = (raw >> 9) & 0x3F; + const mant: u16 = raw & 0x1FF; + + if (exp == 0x3F) return if (sign < 0) -std.math.inf(f32) else std.math.inf(f32); + + const exp_f: f32 = @floatFromInt(@as(i32, @intCast(exp)) - 31); + const mant_f: f32 = 0.5 + @as(f32, @floatFromInt(mant)) / 512.0; + return sign * mant_f * std.math.pow(f32, 2.0, exp_f); +} + +pub fn encodeBF16(v: f32) u16 { + const bits: u32 = @bitCast(v); + return @intCast(bits >> 16); +} + +pub fn decodeBF16(raw: u16) f32 { + const bits: u32 = @as(u32, raw) << 16; + return @bitCast(bits); +} + +test "f32 to GF16 roundtrip" { + const values = [_]f32{ 0.5, 1.0, 2.0, 3.14, 100.0 }; + for (values) |v| { + const encoded = encodeF32ToGF16(v); + const decoded = decodeGF16ToF32(encoded); + const err = @abs(v - decoded) / v; + try std.testing.expect(err < 0.01); + } +} + +test "BF16 roundtrip" { + const values = [_]f32{ 0.5, 1.0, 2.0, 3.14 }; + for (values) |v| { + const encoded = encodeBF16(v); + const decoded = decodeBF16(encoded); + const err = @abs(v - decoded) / v; + try std.testing.expect(err < 0.01); + } +} diff --git a/src/brain/orbitofrontal_value.zig b/src/brain/orbitofrontal_value.zig new file mode 100644 index 0000000000..2634799267 --- /dev/null +++ b/src/brain/orbitofrontal_value.zig @@ -0,0 +1,82 @@ +const std = @import("std"); + +pub const PHI: f64 = 1.6180339887498948482; + +pub const FormatDecision = enum { + gf16, + bf16, + fp16, + ternary, +}; + +pub const LayerAnalysis = struct { + layer_idx: usize, + weight_std: f64, + weight_range: f64, + recommended: FormatDecision, + confidence: f64, +}; + +pub fn recommendFormat(weight_std: f64, weight_range: f64) FormatDecision { + if (weight_std < 0.1) return .ternary; + if (weight_range < 2.0 and weight_std < 0.5) return .gf16; + if (weight_range > 100.0) return .bf16; + return .fp16; +} + +pub fn analyzeLayer(weights: []const f64, layer_idx: usize) LayerAnalysis { + var sum: f64 = 0; + var sum_sq: f64 = 0; + var min_val: f64 = std.math.inf(f64); + var max_val: f64 = -std.math.inf(f64); + + for (weights) |w| { + sum += w; + sum_sq += w * w; + if (w < min_val) min_val = w; + if (w > max_val) max_val = w; + } + + const n: f64 = @floatFromInt(weights.len); + const mean = sum / n; + const variance = sum_sq / n - mean * mean; + const std_dev = @sqrt(@max(variance, 0)); + const range = max_val - min_val; + + const recommended = recommendFormat(std_dev, range); + + var confidence: f64 = 0.5; + switch (recommended) { + .gf16 => confidence = 1.0 - @abs(std_dev - PHI_INV) / PHI_INV, + .ternary => confidence = 1.0 - std_dev, + .bf16 => confidence = @min(range / 1000.0, 0.95), + .fp16 => confidence = 0.7, + } + + return .{ + .layer_idx = layer_idx, + .weight_std = std_dev, + .weight_range = range, + .recommended = recommended, + .confidence = std.math.clamp(confidence, 0.0, 1.0), + }; +} + +test "recommend format for small weights" { + try std.testing.expectEqual(FormatDecision.ternary, recommendFormat(0.05, 0.3)); +} + +test "recommend format for medium weights" { + try std.testing.expectEqual(FormatDecision.gf16, recommendFormat(0.3, 1.5)); +} + +test "recommend format for large range" { + try std.testing.expectEqual(FormatDecision.bf16, recommendFormat(5.0, 500.0)); +} + +test "analyze layer" { + const weights = [_]f64{ 0.1, 0.2, -0.1, 0.3, -0.2, 0.05 }; + const analysis = analyzeLayer(&weights, 0); + try std.testing.expect(analysis.weight_std > 0); + try std.testing.expect(analysis.weight_range > 0); +} diff --git a/src/brain/weber_tuning.zig b/src/brain/weber_tuning.zig new file mode 100644 index 0000000000..eba55b50db --- /dev/null +++ b/src/brain/weber_tuning.zig @@ -0,0 +1,75 @@ +const std = @import("std"); + +pub const PHI: f64 = 1.6180339887498948482; + +pub const WeberLevel = enum(u8) { + fine = 0, + medium = 1, + coarse = 2, +}; + +pub fn weberQuantize(value: f64, jnd_fraction: f64) i2 { + if (@abs(value) < jnd_fraction * 0.5) return 0; + if (value > 0) return 1; + if (value < 0) return -1; + return 0; +} + +pub fn weberQuantizeLevels(value: f64, levels: usize) i64 { + if (value == 0) return 0; + const sign: i64 = if (value > 0) 1 else -1; + const abs_v = @abs(value); + const log_val = std.math.log(f64, std.math.e, abs_v + 1.0); + const quantized = @round(log_val * @as(f64, @floatFromInt(levels)) / 5.0); + return sign * @as(i64, @intFromFloat(@min(@abs(quantized), @as(f64, @floatFromInt(levels))))); +} + +pub fn adaptiveJND(base_jnd: f64, magnitude: f64) f64 { + return base_jnd * (1.0 + std.math.log1p(@abs(magnitude))); +} + +pub fn ternaryEncode(values: []const f64, jnd: f64, output: []i2) void { + for (values, output) |v, *o| { + o.* = weberQuantize(v, jnd); + } +} + +pub fn ternaryStats(encoded: []const i2) struct { pos: usize, neg: usize, zero: usize, sparsity: f64 } { + var pos: usize = 0; + var neg: usize = 0; + var zero: usize = 0; + for (encoded) |v| { + if (v > 0) pos += 1; + if (v < 0) neg += 1; + if (v == 0) zero += 1; + } + return .{ + .pos = pos, + .neg = neg, + .zero = zero, + .sparsity = @as(f64, @floatFromInt(zero)) / @as(f64, @floatFromInt(encoded.len)), + }; +} + +test "weber quantize" { + try std.testing.expectEqual(@as(i2, 1), weberQuantize(0.5, 0.1)); + try std.testing.expectEqual(@as(i2, -1), weberQuantize(-0.5, 0.1)); + try std.testing.expectEqual(@as(i2, 0), weberQuantize(0.01, 0.1)); +} + +test "adaptive JND" { + const jnd_small = adaptiveJND(0.1, 0.1); + const jnd_large = adaptiveJND(0.1, 10.0); + try std.testing.expect(jnd_large > jnd_small); +} + +test "ternary encode and stats" { + const values = [_]f64{ 0.5, -0.3, 0.01, 0.8, -0.02 }; + var encoded: [5]i2 = undefined; + ternaryEncode(&values, 0.1, &encoded); + + const stats = ternaryStats(&encoded); + try std.testing.expectEqual(@as(usize, 2), stats.pos); + try std.testing.expectEqual(@as(usize, 1), stats.neg); + try std.testing.expectEqual(@as(usize, 2), stats.zero); +} diff --git a/src/hslm/cli.zig b/src/hslm/cli.zig index 526c67e98c..4206e6634e 100644 --- a/src/hslm/cli.zig +++ b/src/hslm/cli.zig @@ -1,6 +1,6 @@ const std = @import("std"); pub fn main() !u8 { - std.debug.print("HSLM CLI - Working\\n", .{}); + std.debug.print("HSLM CLI - Working\n", .{}); return 0; }