/** * ModelComplexity - Displays model complexity metrics * Computes total parameters, memory footprint, and summary counts from parsed model data. * Requirements: 20.1, 20.2, 20.3, 20.4, 20.5 */ class ModelComplexity { /** * @param {string} containerId - ID of the container element */ constructor(containerId) { this._containerId = containerId; this._container = document.getElementById(containerId); this._metrics = null; if (!this._container) { console.warn(`[ModelComplexity] Container #${containerId} not found`); } this._setupEventListeners(); } // ─── Private ────────────────────────────────────────────────────────────── /** * Listen for model:loaded events to auto-update metrics. */ _setupEventListeners() { if (window.EventBus && typeof CONFIG !== 'undefined' && CONFIG.EVENTS) { window.EventBus.on(CONFIG.EVENTS.MODEL_LOADED, (data) => { if (data && data.model) { const metrics = this.compute(data.model); this.render(metrics); } }); } } /** * Format a parameter count into a human-readable string. * Examples: 500 → "500", 1500 → "1.5K", 25600000 → "25.6M", 1200000000 → "1.2B" * @param {number} count * @returns {string} */ _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); } // ─── Public API ─────────────────────────────────────────────────────────── /** * Compute complexity metrics from a parsed model. * @param {object} parsedModel * @returns {{ totalParameters: number, memoryFootprint: number, totalNodes: number, totalEdges: number, totalInitializers: number }} */ compute(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); } this._metrics = { totalParameters, memoryFootprint, totalNodes: nodes.length, totalEdges: edges.length, totalInitializers: initializers.length, }; return this._metrics; } /** * Render the complexity metrics into the container. * @param {{ totalParameters: number, memoryFootprint: number, totalNodes: number, totalEdges: number, totalInitializers: number }} metrics */ render(metrics) { if (!this._container) return; if (!metrics) { this._container.innerHTML = '
No complexity metrics available.
'; return; } const { totalParameters, memoryFootprint, totalNodes, totalEdges, totalInitializers } = metrics; const paramDisplay = this._formatParameterCount(totalParameters); const memoryDisplay = (typeof Formatters !== 'undefined' && Formatters.formatBytes) ? Formatters.formatBytes(memoryFootprint) : this._fallbackFormatBytes(memoryFootprint); let html = `Select a model to view complexity metrics
'; } /** * Get the last computed metrics. * @returns {object|null} */ getMetrics() { return this._metrics; } /** * Get the formatted parameter count string (useful for testing). * @param {number} count * @returns {string} */ formatParameterCount(count) { return this._formatParameterCount(count); } } window.ModelComplexity = ModelComplexity;