openskynet / src /skynet /experiments /adaptive_sparse_metabolism_01.ts
Darochin's picture
Add complete Skynet Brain Lab source tree
59936ca verified
import fs from "node:fs/promises";
import path from "node:path";
import { SparseMetabolism, type ComponentType } from "../../omega/sparse-metabolism.js";
type TraceStep = {
latentFrustration: number;
observedFrustration: number;
};
type RunResult = {
seed: number;
ruleAccuracy: number;
adaptiveAccuracy: number;
ruleFalsePositiveRate: number;
adaptiveFalsePositiveRate: number;
ruleMetabolicError: number;
adaptiveMetabolicError: number;
delta: number;
};
const COMPONENTS: ComponentType[] = [
"neural_logic_engine",
"hierarchical_memory",
"lyapunov_controller",
"causal_reasoner",
"autonomy_logger",
"jepa_enhancer",
];
function clamp01(value: number): number {
return Math.max(0, Math.min(1, value));
}
function mulberry32(seed: number): () => number {
let t = seed >>> 0;
return () => {
t += 0x6d2b79f5;
let r = Math.imul(t ^ (t >>> 15), 1 | t);
r ^= r + Math.imul(r ^ (r >>> 7), 61 | r);
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
};
}
function gaussian(rand: () => number, mean = 0, std = 1): number {
const u1 = Math.max(rand(), 1e-7);
const u2 = Math.max(rand(), 1e-7);
const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
return mean + z0 * std;
}
function generateTrace(seed: number, length: number): TraceStep[] {
const rand = mulberry32(seed);
const trace: TraceStep[] = [];
let latent = 0.18 + rand() * 0.1;
for (let i = 0; i < length; i += 1) {
const shock =
rand() < 0.08 ? (rand() < 0.5 ? -0.35 : 0.35) : rand() < 0.16 ? gaussian(rand, 0, 0.18) : 0;
const drift = gaussian(rand, 0, 0.05);
latent = clamp01(latent * 0.82 + 0.12 + drift + shock);
const observed = clamp01(latent + gaussian(rand, 0, 0.12));
trace.push({
latentFrustration: latent,
observedFrustration: observed,
});
}
return trace;
}
function classifyAdaptiveFrustration(params: {
priorEffective?: number;
priorObserved?: number;
observed: number;
}): { effective: number; retention: number } {
const priorEffective = params.priorEffective ?? params.observed;
const priorObserved = params.priorObserved ?? params.observed;
const flux = Math.abs(params.observed - priorObserved);
const modulation = 1 / (1 + Math.exp(-(flux - 0.12) * 10));
const retention = clamp01(Math.max(0.58, Math.min(0.96, 0.9 - modulation * 0.28)));
return {
effective: clamp01(retention * priorEffective + (1 - retention) * params.observed),
retention,
};
}
function stateForFrustration(metabolism: SparseMetabolism, frustration: number) {
return metabolism.computeMetabolism(frustration);
}
function stepAccuracy(predicted: Set<ComponentType>, expected: Set<ComponentType>): number {
let correct = 0;
for (const component of COMPONENTS) {
correct += Number(predicted.has(component) === expected.has(component));
}
return correct / COMPONENTS.length;
}
function evaluateTrace(trace: TraceStep[]): {
ruleAccuracy: number;
adaptiveAccuracy: number;
ruleFalsePositiveRate: number;
adaptiveFalsePositiveRate: number;
ruleMetabolicError: number;
adaptiveMetabolicError: number;
} {
const expectedMetabolism = new SparseMetabolism();
const ruleMetabolism = new SparseMetabolism();
const adaptiveMetabolism = new SparseMetabolism();
let adaptiveEffective: number | undefined;
let adaptiveObserved: number | undefined;
let ruleAccuracy = 0;
let adaptiveAccuracy = 0;
let ruleFalsePositives = 0;
let adaptiveFalsePositives = 0;
let expectedInactiveCount = 0;
let ruleMetabolicError = 0;
let adaptiveMetabolicError = 0;
for (const step of trace) {
const expectedState = stateForFrustration(expectedMetabolism, step.latentFrustration);
const expected = new Set(expectedState.activatedComponents);
const ruleState = stateForFrustration(ruleMetabolism, step.observedFrustration);
const rule = new Set(ruleState.activatedComponents);
const adaptiveFrustration = classifyAdaptiveFrustration({
priorEffective: adaptiveEffective,
priorObserved: adaptiveObserved,
observed: step.observedFrustration,
});
adaptiveEffective = adaptiveFrustration.effective;
adaptiveObserved = step.observedFrustration;
const adaptiveState = stateForFrustration(adaptiveMetabolism, adaptiveFrustration.effective);
const adaptive = new Set(adaptiveState.activatedComponents);
ruleAccuracy += stepAccuracy(rule, expected);
adaptiveAccuracy += stepAccuracy(adaptive, expected);
ruleMetabolicError += Math.abs(ruleState.totalMetabolicRate - expectedState.totalMetabolicRate);
adaptiveMetabolicError += Math.abs(
adaptiveState.totalMetabolicRate - expectedState.totalMetabolicRate,
);
for (const component of COMPONENTS) {
const expectedActive = expected.has(component);
if (!expectedActive) {
expectedInactiveCount += 1;
ruleFalsePositives += Number(rule.has(component));
adaptiveFalsePositives += Number(adaptive.has(component));
}
}
}
return {
ruleAccuracy: ruleAccuracy / trace.length,
adaptiveAccuracy: adaptiveAccuracy / trace.length,
ruleFalsePositiveRate: ruleFalsePositives / Math.max(1, expectedInactiveCount),
adaptiveFalsePositiveRate: adaptiveFalsePositives / Math.max(1, expectedInactiveCount),
ruleMetabolicError: ruleMetabolicError / trace.length,
adaptiveMetabolicError: adaptiveMetabolicError / trace.length,
};
}
async function main() {
const seeds = [101, 202, 303, 404, 505];
const results: RunResult[] = [];
for (const seed of seeds) {
const trace = generateTrace(seed, 240);
const result = evaluateTrace(trace);
results.push({
seed,
ruleAccuracy: result.ruleAccuracy,
adaptiveAccuracy: result.adaptiveAccuracy,
ruleFalsePositiveRate: result.ruleFalsePositiveRate,
adaptiveFalsePositiveRate: result.adaptiveFalsePositiveRate,
ruleMetabolicError: result.ruleMetabolicError,
adaptiveMetabolicError: result.adaptiveMetabolicError,
delta: result.adaptiveAccuracy - result.ruleAccuracy,
});
}
const report = {
experiment: "adaptive_sparse_metabolism_01",
runs: results,
meanRuleAccuracy: results.reduce((sum, run) => sum + run.ruleAccuracy, 0) / results.length,
meanAdaptiveAccuracy:
results.reduce((sum, run) => sum + run.adaptiveAccuracy, 0) / results.length,
meanRuleFalsePositiveRate:
results.reduce((sum, run) => sum + run.ruleFalsePositiveRate, 0) / results.length,
meanAdaptiveFalsePositiveRate:
results.reduce((sum, run) => sum + run.adaptiveFalsePositiveRate, 0) / results.length,
meanRuleMetabolicError:
results.reduce((sum, run) => sum + run.ruleMetabolicError, 0) / results.length,
meanAdaptiveMetabolicError:
results.reduce((sum, run) => sum + run.adaptiveMetabolicError, 0) / results.length,
meanDelta: results.reduce((sum, run) => sum + run.delta, 0) / results.length,
};
const outputPath = path.join(
process.cwd(),
".openskynet",
"skynet-experiments",
"adaptive_sparse_metabolism_01.json",
);
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, JSON.stringify(report, null, 2), "utf-8");
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
}
await main();