205 """Render all four chart panels for the given simulation time.
207 Chart layout (top to bottom)
208 ----------------------------
209 1. Live qubit count over time (step function).
210 2. Entangled states over time (step function).
211 3. Total measurements over time (step function).
212 4. Per-node qubit distribution at ``snap.t_ns`` (bar chart).
214 Cached series are rebuilt only when a new trace is loaded, detected
215 by an identity change on ``state_manager.time_array``. The shared
216 state manager is never queried here: the controller performs the
217 per-frame ``snapshot_at()`` call and passes the resulting
218 ``Snapshot``, which drives both the bar chart and the current-time
221 @param state_manager Shared ``SimulationStateManager`` instance,
222 read only for its event list and timeline.
223 @param snap ``Snapshot`` at the current simulation time.
225 current_time = snap.t_ns
228 _fig_bg =
"#161b22" if _dark
else "#ffffff"
229 _ax_bg =
"#1c2128" if _dark
else "#ffffff"
230 _spine_c =
"#8d96a0" if _dark
else "#24292f"
231 _grid_c =
"#30363d" if _dark
else "#f0f2f5"
232 _text_c =
"#e6edf3" if _dark
else "#24292f"
233 _marker_c =
"#8d96a0" if _dark
else "#555555"
234 self.
fig.set_facecolor(_fig_bg)
235 if not state_manager.time_array:
245 current_node_qubits: dict[str, int] = {
248 for q
in snap.qubits.values()
249 if q.node == node_name
250 and q.label
in snap.live_qubit_labels
251 and q.label
not in snap.inflight_qubits
253 for node_name
in snap.nodes
255 current_inflight_count: int = len(snap.inflight_qubits)
256 current_loss_count: int = len(snap.lost_qubits)
259 "axes.linewidth": 0.95,
260 "xtick.major.width": 0.95,
261 "ytick.major.width": 0.95,
262 "xtick.major.size": 0,
263 "ytick.major.size": 0,
265 with matplotlib.rc_context(rc_params):
266 ax1 = self.
fig.add_subplot(4, 1, 1)
267 ax2 = self.
fig.add_subplot(4, 1, 2)
268 ax3 = self.
fig.add_subplot(4, 1, 3)
269 ax4 = self.
fig.add_subplot(4, 1, 4)
270 times_us = [t / 1000
for t
in state_manager.time_array]
271 current_us = current_time / 1000
273 colors = [
"#0072B2",
"#E69F00",
"#009E73",
"#D55E00",
"#CC79A7",
"#56B4E8"]
275 spine_color = _spine_c
278 current_line = {
"color": _marker_c,
"linestyle":
"--",
"linewidth": 1.2,
"alpha": 0.6}
280 def style_ax(ax, show_xlabels=True):
281 for spine
in ax.spines.values():
282 spine.set_visible(
True)
283 spine.set_color(spine_color)
284 spine.set_linewidth(0.95)
289 labelbottom=show_xlabels,
293 labelcolor=text_color,
297 axis=
"y", which=
"major", length=0, width=0, color=spine_color, labelcolor=text_color, pad=4
299 ax.yaxis.grid(
True, linestyle=
"-", linewidth=0.6, alpha=1.0, color=grid_color)
301 ax.set_axisbelow(
True)
302 ax.set_facecolor(_ax_bg)
304 ax.title.set_color(text_color)
305 ax.xaxis.label.set_color(text_color)
306 ax.yaxis.label.set_color(text_color)
308 _lx, _ly = _expand_series(state_manager.time_array, live_qubits, operation_windows)
309 ax1.plot([t / 1000
for t
in _lx], _ly, color=colors[0], linewidth=line_width)
310 ax1.axvline(current_us, **current_line)
311 ax1.set_ylabel(
"Num. Qubits", fontsize=8.5)
313 f
"Live Qubits \u2014 t\u2009=\u2009{current_us:.3f}\u202f\u03bcs",
318 ax1.yaxis.set_major_locator(
319 FixedLocator(_make_ticks(min(live_qubits), max(live_qubits), integer=
True))
321 ax1.xaxis.set_major_locator(FixedLocator(_make_ticks(times_us[0], times_us[-1], n_inner=0)))
322 style_ax(ax1, show_xlabels=
True)
324 _ex, _ey = _expand_series(state_manager.time_array, entangled_states, operation_windows)
325 ax2.plot([t / 1000
for t
in _ex], _ey, color=colors[1], linewidth=line_width)
326 ax2.axvline(current_us, **current_line)
327 ax2.set_ylabel(
"Num. States", fontsize=8.5)
329 f
"Entangled States \u2014 t\u2009=\u2009{current_us:.3f}\u202f\u03bcs",
334 ax2.yaxis.set_major_locator(
335 FixedLocator(_make_ticks(min(entangled_states), max(entangled_states), integer=
True))
337 ax2.xaxis.set_major_locator(FixedLocator(_make_ticks(times_us[0], times_us[-1], n_inner=0)))
338 style_ax(ax2, show_xlabels=
True)
340 _mx, _my = _expand_series(state_manager.time_array, total_measurements, operation_windows)
341 ax3.plot([t / 1000
for t
in _mx], _my, color=colors[2], linewidth=line_width)
342 ax3.axvline(current_us, **current_line)
343 ax3.set_ylabel(
"Num. Measurements", fontsize=8.5)
344 ax3.set_xlabel(
"Time (\u03bcs)", fontsize=8.5)
346 f
"Total Measurements \u2014 t\u2009=\u2009{current_us:.3f}\u202f\u03bcs",
351 ax3.yaxis.set_major_locator(
352 FixedLocator(_make_ticks(min(total_measurements), max(total_measurements), integer=
True))
354 ax3.xaxis.set_major_locator(FixedLocator(_make_ticks(times_us[0], times_us[-1], n_inner=0)))
357 node_names = list(state_manager.nodes.keys())
358 bar_counts = [current_node_qubits.get(name, 0)
for name
in node_names]
359 bar_colors = [colors[i % len(colors)]
for i
in range(len(node_names))]
360 all_bar_names = [*node_names,
"In-Flight",
"Lost"]
361 all_bar_counts = [*bar_counts, current_inflight_count, current_loss_count]
362 all_bar_colors = [*bar_colors,
"#999999",
"#D55E00"]
364 all_bar_names, all_bar_counts, color=all_bar_colors, width=0.52, edgecolor=
"none", linewidth=0
366 ax4.set_ylabel(
"Num. Qubits", fontsize=8.5)
367 ax4.set_xlabel(
"Node", fontsize=8.5)
369 f
"Live Qubits per Node \u2014 t\u2009=\u2009{current_us:.3f}\u202f\u03bcs",
374 ax4.set_ylim(bottom=0)
375 max_bar = max(all_bar_counts)
if all_bar_counts
else 0
376 ax4.yaxis.set_major_locator(FixedLocator(_make_ticks(0, max_bar, integer=
True)))
377 style_ax(ax4, show_xlabels=
True)
378 if len(all_bar_names) > 5:
379 ax4.tick_params(axis=
"x", rotation=30)
381 self.
fig.set_layout_engine(
"constrained")