File size: 5,753 Bytes
9bd422a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/**
 * 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;