Spaces:
Running
Running
| /** | |
| * 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'); | |
| }); | |
| }); | |