Spaces:
Running
Running
| /** | |
| * 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 = '<p class="text-muted">No complexity metrics available.</p>'; | |
| 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 = ` | |
| <div class="mb-3"> | |
| <div class="d-flex flex-wrap gap-3 small"> | |
| <span><i class="fas fa-project-diagram me-1"></i><strong>${totalNodes}</strong> Nodes</span> | |
| <span><i class="fas fa-arrows-alt-h me-1"></i><strong>${totalEdges}</strong> Edges</span> | |
| <span><i class="fas fa-database me-1"></i><strong>${totalInitializers}</strong> Initializers</span> | |
| </div> | |
| </div> | |
| <div class="d-flex flex-wrap gap-3"> | |
| <div class="card p-2 flex-fill text-center"> | |
| <div class="small text-muted">Parameters</div> | |
| <div class="fw-bold">${paramDisplay === '0' ? '0 parameters' : paramDisplay}</div> | |
| </div> | |
| <div class="card p-2 flex-fill text-center"> | |
| <div class="small text-muted">Memory Footprint</div> | |
| <div class="fw-bold">${memoryDisplay}</div> | |
| </div> | |
| </div>`; | |
| this._container.innerHTML = html; | |
| } | |
| /** | |
| * Fallback byte formatter when Formatters utility is not available. | |
| * @param {number} bytes | |
| * @returns {string} | |
| */ | |
| _fallbackFormatBytes(bytes) { | |
| if (bytes == null || isNaN(bytes) || bytes === 0) return '0 B'; | |
| const k = 1024; | |
| const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; | |
| const i = Math.min(Math.floor(Math.log(Math.abs(bytes)) / Math.log(k)), sizes.length - 1); | |
| return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]; | |
| } | |
| /** | |
| * Clear the display. | |
| */ | |
| clear() { | |
| this._metrics = null; | |
| if (!this._container) return; | |
| this._container.innerHTML = '<p class="text-muted">Select a model to view complexity metrics</p>'; | |
| } | |
| /** | |
| * 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; | |