memlnaut-nisps/manifold/tests/io-reshape.test.ts

50 lines
1.9 KiB
TypeScript
Raw Normal View History

import { expect, test } from 'bun:test';
import type { MLArchitecture } from '../src/engine/types';
import {
completeDimensionMap,
remapFlatWeights,
remapVector,
resizeTarget,
} from '../src/engine/io-reshape';
function arch(inputSize: number, outputSize: number): MLArchitecture {
return { inputSize, hidden: [2, 2, 2], outputSize, numLayers: 4, maxExamples: 8 };
}
test('capacity policy only reconstructs when active I/O exceeds capacity', () => {
expect(resizeTarget(2, 8, 'capacity')).toBeNull();
expect(resizeTarget(9, 8, 'capacity')).toBe(9);
expect(resizeTarget(2, 8, 'exact')).toBe(2);
expect(resizeTarget(8, 8, 'exact')).toBeNull();
});
test('a middle deletion keeps surviving identities and moves unused slots to the tail', () => {
expect(completeDimensionMap([0, 2], 4, 4)).toEqual([0, 2, 1, 3]);
});
test('example vectors remove deleted dimensions and fill additions with placeholders', () => {
expect(Array.from(remapVector([10, 20, 30], [0, 2], 2, -1))).toEqual([10, 30]);
expect(Array.from(remapVector([10, 20], [0, null, 1], 3, 0.5))).toEqual([10, 0.5, 20]);
});
test('weight remap preserves an arbitrary output row and bias by identity', () => {
const oldArch = arch(2, 3);
const newArch = arch(2, 2);
// 4 layers: weights 4 + 4 + 4 + 6, then biases 2 + 2 + 2 + 3 = 27.
const oldWeights = Float32Array.from({ length: 27 }, (_, i) => i + 1);
const freshWeights = new Float32Array(24).fill(-1);
const remapped = remapFlatWeights(
oldWeights,
oldArch,
freshWeights,
newArch,
undefined,
[0, 2],
);
// Final-layer weights begin at 12. Old rows: [13,14], [15,16], [17,18].
expect(Array.from(remapped.slice(12, 16))).toEqual([13, 14, 17, 18]);
// Destination output biases are the final two entries; old output biases
// are [25,26,27], so output identity 2 must retain 27 rather than 26.
expect(Array.from(remapped.slice(22, 24))).toEqual([25, 27]);
});