model-explorer / js /tests /modelComplexity.test.js
mr4's picture
Upload 71 files
9bd422a verified
/**
* Unit tests for ModelComplexity.compute() and _formatParameterCount()
* Validates: Requirements 20.1, 20.2, 20.3, 20.4, 20.5
*/
import { describe, it, expect } from 'vitest';
// Mirror the pure compute logic from ModelComplexity
function computeComplexity(parsedModel) {
const nodes = (parsedModel && parsedModel.graph && parsedModel.graph.nodes) || [];
const edges = (parsedModel && parsedModel.graph && parsedModel.graph.edges) || [];
const initializers = (parsedModel && parsedModel.initializers) || [];
let totalParameters = 0;
let memoryFootprint = 0;
for (const init of initializers) {
totalParameters += (init.elementCount || 0);
memoryFootprint += (init.size || 0);
}
return {
totalParameters,
memoryFootprint,
totalNodes: nodes.length,
totalEdges: edges.length,
totalInitializers: initializers.length,
};
}
// Mirror the formatting logic
function formatParameterCount(count) {
if (count == null || isNaN(count) || count === 0) return '0';
const abs = Math.abs(count);
if (abs >= 1e9) return (count / 1e9).toFixed(1).replace(/\.0$/, '') + 'B';
if (abs >= 1e6) return (count / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
if (abs >= 1e3) return (count / 1e3).toFixed(1).replace(/\.0$/, '') + 'K';
return String(count);
}
describe('ModelComplexity.compute', () => {
it('should sum elementCount from all initializers as totalParameters', () => {
const model = {
graph: { nodes: [{ id: '1', opType: 'Conv' }], edges: [] },
initializers: [
{ name: 'w1', elementCount: 1000, size: 4000 },
{ name: 'w2', elementCount: 500, size: 2000 },
{ name: 'b1', elementCount: 64, size: 256 },
],
};
const m = computeComplexity(model);
expect(m.totalParameters).toBe(1564);
expect(m.memoryFootprint).toBe(6256);
expect(m.totalInitializers).toBe(3);
});
it('should sum size from all initializers as memoryFootprint', () => {
const model = {
graph: { nodes: [], edges: [] },
initializers: [
{ name: 'w1', elementCount: 100, size: 400 },
{ name: 'w2', elementCount: 200, size: 800 },
],
};
const m = computeComplexity(model);
expect(m.memoryFootprint).toBe(1200);
});
it('should count nodes, edges, and initializers', () => {
const model = {
graph: {
nodes: [{ id: '1' }, { id: '2' }, { id: '3' }],
edges: [{ source: '1', target: '2' }, { source: '2', target: '3' }],
},
initializers: [{ name: 'w1', elementCount: 10, size: 40 }],
};
const m = computeComplexity(model);
expect(m.totalNodes).toBe(3);
expect(m.totalEdges).toBe(2);
expect(m.totalInitializers).toBe(1);
});
it('should handle no initializers (0 parameters)', () => {
const model = {
graph: { nodes: [{ id: '1' }], edges: [] },
initializers: [],
};
const m = computeComplexity(model);
expect(m.totalParameters).toBe(0);
expect(m.memoryFootprint).toBe(0);
expect(m.totalInitializers).toBe(0);
});
it('should handle null/undefined model', () => {
const m = computeComplexity(null);
expect(m.totalParameters).toBe(0);
expect(m.memoryFootprint).toBe(0);
expect(m.totalNodes).toBe(0);
expect(m.totalEdges).toBe(0);
expect(m.totalInitializers).toBe(0);
});
it('should handle initializers with missing elementCount or size', () => {
const model = {
graph: { nodes: [], edges: [] },
initializers: [
{ name: 'w1' },
{ name: 'w2', elementCount: 100 },
{ name: 'w3', size: 500 },
],
};
const m = computeComplexity(model);
expect(m.totalParameters).toBe(100);
expect(m.memoryFootprint).toBe(500);
});
});
describe('formatParameterCount', () => {
it('should return "0" for zero', () => {
expect(formatParameterCount(0)).toBe('0');
});
it('should return plain number for counts under 1000', () => {
expect(formatParameterCount(500)).toBe('500');
expect(formatParameterCount(999)).toBe('999');
});
it('should format thousands with K suffix', () => {
expect(formatParameterCount(1500)).toBe('1.5K');
expect(formatParameterCount(10000)).toBe('10K');
expect(formatParameterCount(999999)).toBe('1000K');
});
it('should format millions with M suffix', () => {
expect(formatParameterCount(1000000)).toBe('1M');
expect(formatParameterCount(25600000)).toBe('25.6M');
expect(formatParameterCount(138000000)).toBe('138M');
});
it('should format billions with B suffix', () => {
expect(formatParameterCount(1200000000)).toBe('1.2B');
expect(formatParameterCount(7000000000)).toBe('7B');
});
it('should return "0" for null/undefined/NaN', () => {
expect(formatParameterCount(null)).toBe('0');
expect(formatParameterCount(undefined)).toBe('0');
expect(formatParameterCount(NaN)).toBe('0');
});
});