| """ |
| Shared matplotlib styling primitives: palette, axis styling, and the |
| time-series plot helpers used across the panel builders. |
| |
| Split out of the former monolithic dashboard_core.py (Phase 1 of the |
| dashboard refactor). _series_plot/_series_enhanced/_energy_enhanced's |
| former redundancy was unified into a single _series_plot (Phase 2) -- |
| same visual output, one parameterized function instead of three. |
| """ |
|
|
| import numpy as np |
| import pandas as pd |
| import matplotlib.pyplot as plt |
| import matplotlib.patches as mpatches |
| from matplotlib.colors import LinearSegmentedColormap |
|
|
| C = dict( |
| bg = '#03050e', |
| panel = '#070b18', |
| grid = '#0f1628', |
| border = '#18243c', |
| title = '#e2eaf8', |
| label = '#4a6080', |
| tick = '#2e4a6a', |
| prob = '#38bdf8', |
| energy = '#00e0ff', |
| entropy = '#f43f7a', |
| purity = '#4ade80', |
| grad = '#fbbf24', |
| noise = '#fb923c', |
| theta = '#a78bfa', |
| dom = '#ffffff', |
| accent = '#00ff9d', |
| warn = '#ff6b35', |
| ) |
|
|
| MONO = {'fontfamily': 'monospace'} |
| FILL_A = 0.08 |
|
|
| _cmap_prob = LinearSegmentedColormap.from_list( |
| 'tureq_prob', ['#0a1f3d', '#38bdf8', '#f43f7a'], N=256 |
| ) |
|
|
| BG_ART = '#030305' |
| PANEL_ART = '#07070a' |
| CYAN_N = '#00f3ff' |
| PINK_N = '#ff0055' |
| PURP_N = '#7a00ff' |
| GOLD_N = '#ffaa00' |
|
|
| |
| |
| |
| |
| FIG_BG_DARK = '#010409' |
|
|
|
|
| def _ax_style(ax, title='', xlabel='', ylabel='', spine_alpha=0.6): |
| ax.set_facecolor(C['panel']) |
| for sp in ax.spines.values(): |
| sp.set_edgecolor(C['border']) |
| sp.set_linewidth(0.7) |
| sp.set_alpha(spine_alpha) |
| ax.tick_params(colors=C['tick'], which='both', length=3, width=0.5, labelsize=7.5) |
| ax.xaxis.label.set_color(C['label']); ax.xaxis.label.set_fontsize(8) |
| ax.yaxis.label.set_color(C['label']); ax.yaxis.label.set_fontsize(8) |
| ax.grid(True, ls='--', lw=0.30, alpha=0.25, color=C['grid']) |
| if title: |
| ax.set_title(title, color=C['title'], fontsize=9.5, fontweight='bold', |
| pad=6, loc='left', **MONO) |
| if xlabel: ax.set_xlabel(xlabel, **MONO) |
| if ylabel: ax.set_ylabel(ylabel, **MONO) |
|
|
|
|
| def _fill(ax, x, y, col): |
| ax.fill_between(x, y, alpha=FILL_A, color=col) |
|
|
|
|
| def _rolling(y, win): |
| return pd.Series(y).rolling(win, center=True, min_periods=1).mean().values |
|
|
|
|
| def _badge(ax, text, color): |
| """Top-right value badge.""" |
| ax.text(0.98, 0.97, text, |
| transform=ax.transAxes, ha='right', va='top', |
| fontsize=7.5, color=color, **MONO, |
| bbox=dict(boxstyle='round,pad=0.25', |
| facecolor=C['bg'], edgecolor=C['border'], alpha=0.72)) |
|
|
|
|
| def _series_plot(ax, df, col, color, title, xlabel, ylabel, *, |
| show_raw=True, badge=False, badge_fmt='{:.4g}', |
| ref_line=None, detect_plateau=False, |
| mark_convergence=False): |
| """Unified time-series: raw line + fill + rolling mean + last-value |
| annotation, with optional badge / reference-line / barren-plateau-span / |
| convergence-marker overlays. |
| |
| Unifies what used to be three near-duplicate functions (_series_plot, |
| which this absorbs, plus _series_enhanced and _energy_enhanced, Phase 2 |
| of the dashboard refactor) into one parameterized function -- same |
| visual output as each of the three original call shapes: |
| - old _series_plot(...) == _series_plot(...) (defaults) |
| - old _series_enhanced(..., ref_line=X) == _series_plot(..., badge=True, ref_line=X) |
| - old _energy_enhanced(ax, df) == _series_plot(ax, df, 'VQE_Energy', |
| C['energy'], 'VQE Energy', 'Epoch', |
| 'E (Ha)', badge=True, |
| badge_fmt='Eₙ={:.4g} Ha', |
| mark_convergence=True) |
| """ |
| _ax_style(ax, title, xlabel, ylabel) |
| if df.empty or col not in df.columns: |
| ax.axis('off') |
| ax.text(0.5, 0.5, f'[ {col} — no data ]', |
| ha='center', va='center', color=C['label'], |
| fontsize=8.5, transform=ax.transAxes, **MONO) |
| return |
| x = df.index.values |
| y = df[col].values |
| if show_raw: |
| ax.plot(x, y, color=color, lw=1.4, alpha=0.7) |
| _fill(ax, x, y, color) |
| win = max(3, len(y) // 15) |
| rm = _rolling(y, win) |
| ax.plot(x, rm, color=C['title'], lw=1.1, alpha=0.6, ls='--', |
| label='rolling mean') |
| ax.annotate(f'{y[-1]:.4g}', |
| xy=(x[-1], y[-1]), xytext=(-4, 6), |
| textcoords='offset points', |
| color=color, fontsize=7.5, fontweight='bold', |
| ha='right', **MONO) |
|
|
| x_f = df.index.to_numpy(dtype=float) |
|
|
| if badge: |
| _badge(ax, badge_fmt.format(y[-1]), color) |
|
|
| if ref_line is not None: |
| ax.axhline(ref_line, color=color, lw=0.6, ls='--', alpha=0.28) |
|
|
| if detect_plateau: |
| _thr = 0.01 * np.abs(y).max() if np.abs(y).max() > 0 else 1e-9 |
| _bp = np.abs(y) < _thr |
| if _bp.sum() > 3: |
| _edges = np.where(np.diff(_bp.astype(int)))[0] |
| if len(_edges) >= 2: |
| ax.axvspan(x_f[_edges[0]], x_f[_edges[1]], |
| alpha=0.11, color=C['warn']) |
| ax.text( |
| (x_f[_edges[0]] + x_f[_edges[1]]) / 2, |
| y.max() * 0.88, |
| 'plateau', ha='center', fontsize=6.5, |
| color=C['warn'], alpha=0.80, **MONO, |
| ) |
|
|
| if mark_convergence and len(y) > 2: |
| _conv = int(np.argmin(np.gradient(y))) |
| ax.axvline(x_f[_conv], color=C['warn'], lw=0.8, ls=':', alpha=0.55) |
| ax.annotate( |
| f'∇min@{_conv}', |
| xy=(x_f[_conv], y[_conv]), |
| xytext=(6, -14), textcoords='offset points', |
| color=C['warn'], fontsize=7.0, |
| arrowprops=dict(arrowstyle='-', color=C['warn'], lw=0.5), |
| **MONO, |
| ) |
|
|
|
|
| def _interp_colour(c1_hex, c2_hex, t): |
| t = float(np.clip(t, 0, 1)) |
| def h(s): return [int(s.lstrip('#')[i:i+2], 16) / 255 for i in (0, 2, 4)] |
| r1, g1, b1 = h(c1_hex) |
| r2, g2, b2 = h(c2_hex) |
| to_hex = lambda v: f'{int(v*255):02x}' |
| return f'#{to_hex(r1+(r2-r1)*t)}{to_hex(g1+(g2-g1)*t)}{to_hex(b1+(b2-b1)*t)}' |
|
|
|
|
| def _noise_profile_plot(ax, noise_model, noise_p, n_qubits, |
| prob_ideal, prob_noisy=None): |
| _ax_style(ax, 'Noise Analysis', '', '') |
| ax.set_xlim(0, 1); ax.set_ylim(-0.08, 1.08) |
| ax.axis('off') |
|
|
| model_col = C['warn'] if noise_model != 'ideal' else C['accent'] |
| ax.text(0.5, 0.97, noise_model.upper().replace('_', ' '), |
| ha='center', va='top', color=model_col, |
| fontsize=16, fontweight='bold', transform=ax.transAxes, **MONO) |
|
|
| bar_y = 0.80 |
| ax.add_patch(mpatches.FancyBboxPatch( |
| (0.05, bar_y - 0.025), 0.90, 0.05, |
| boxstyle='round,pad=0.005', |
| facecolor=C['grid'], edgecolor=C['border'], lw=0.8, |
| transform=ax.transAxes, zorder=2)) |
| fill_w = 0.90 * min(noise_p / 0.10, 1.0) |
| if fill_w > 0: |
| fill_col = _interp_colour('#00ff9d', '#ff6b35', noise_p / 0.10) |
| ax.add_patch(mpatches.FancyBboxPatch( |
| (0.05, bar_y - 0.025), fill_w, 0.05, |
| boxstyle='round,pad=0.005', |
| facecolor=fill_col, edgecolor='none', |
| transform=ax.transAxes, zorder=3)) |
| ax.text(0.5, bar_y + 0.06, f'p = {noise_p:.4f}', |
| ha='center', va='bottom', color=C['title'], |
| fontsize=11, fontweight='bold', transform=ax.transAxes, **MONO) |
| ax.text(0.05, bar_y - 0.07, '0', ha='left', va='top', |
| color=C['label'], fontsize=7.5, transform=ax.transAxes, **MONO) |
| ax.text(0.95, bar_y - 0.07, '0.10', ha='right', va='top', |
| color=C['label'], fontsize=7.5, transform=ax.transAxes, **MONO) |
|
|
| if prob_noisy is not None and noise_model != 'ideal': |
| fid_bc = float(np.sum(np.sqrt(np.maximum(prob_ideal, 0) * |
| np.maximum(prob_noisy, 0)))) |
| tvd = float(0.5 * np.sum(np.abs(prob_ideal - prob_noisy))) |
| rows = [ |
| ('Bhattacharyya Fidelity', f'{fid_bc:.6f}', C['purity']), |
| ('Total Variation Distance', f'{tvd:.6f}', C['entropy']), |
| ('Noise Channel', noise_model.replace('_', ' '), C['noise']), |
| ] |
| else: |
| rows = [ |
| ('Channel', 'ideal — no noise applied', C['accent']), |
| ('Fidelity', '1.000000', C['purity']), |
| ('TVD', '0.000000', C['label']), |
| ] |
| for j, (k, v, col) in enumerate(rows): |
| yy = 0.58 - j * 0.14 |
| ax.text(0.03, yy, k, ha='left', va='center', color=C['label'], |
| fontsize=8, transform=ax.transAxes, **MONO) |
| ax.text(0.97, yy, v, ha='right', va='center', color=col, |
| fontsize=8.5, fontweight='bold', |
| transform=ax.transAxes, **MONO) |
| ax.axhline(yy - 0.05, xmin=0.01, xmax=0.99, color=C['grid'], lw=0.35) |
|
|
| if noise_model in ('ideal', 'depolarizing'): |
| ins = ax.inset_axes([0.04, 0.04, 0.92, 0.26]) |
| ins.set_facecolor(C['panel']) |
| for sp in ins.spines.values(): |
| sp.set_edgecolor(C['border']); sp.set_linewidth(0.5) |
| ps = np.linspace(0, 0.1, 200) |
| d = 2 ** n_qubits |
| fid_curve = ((1.0 - ps * (d - 1) / d) ** n_qubits).clip(0, 1) |
| ins.plot(ps, fid_curve, color=C['purity'], lw=1.2) |
| ins.axvline(noise_p, color=C['warn'], lw=0.9, ls='--', alpha=0.8) |
| ins.fill_between(ps, fid_curve, alpha=0.07, color=C['purity']) |
| ins.set_xlim(0, 0.10); ins.set_ylim(0, 1.05) |
| ins.tick_params(colors=C['tick'], labelsize=6.5, length=2) |
| ins.set_xlabel('p', color=C['label'], fontsize=7, **MONO) |
| ins.set_ylabel('F(p)', color=C['label'], fontsize=7, **MONO) |
| ins.set_title('Theoretical Fidelity Curve', |
| color=C['label'], fontsize=7, pad=2, **MONO) |
| ins.grid(True, ls=':', lw=0.3, alpha=0.25, color=C['grid']) |
|
|