Content is user-generated and unverified.

remixed-3911a0ff (2).tsx — Revision History

Documentation of all changes from the original baseline through the current version. Most recent first. Each revision is staged as a single drop-in replacement file; there is no git history maintained on my side.

To check which revision you have locally:

bash
wc -l "/Users/yao-jenchang/Downloads/remixed-3911a0ff (2).tsx"
grep -c "cmaESAsync" "/Users/yao-jenchang/Downloads/remixed-3911a0ff (2).tsx"
grep -c "highClampHits \* clampHits" "/Users/yao-jenchang/Downloads/remixed-3911a0ff (2).tsx"

The first command should give a line count matching the current version below. The latter two should each return ≥1 if you're on R8.


R8 — Quadratic clamp penalty (current)

File: 3091 lines · 165 KB · 990-line diff vs. baseline.

penClamp scales as 200 · highClampHits² instead of flat 200. Mild grazing of BVdss (1–2 sample hits per HB period) costs the same as before; deep clamping (10+ hits) costs 20k+, dominating the cost function so CMA-ES is pushed out of clamped basins entirely.

The expectation: if a previous run stopped with Status: BVdss CLAMPED and PAE ≈ 27% at iter 400, the next run with R8 should either (a) find a non-clamped local optimum with comparable or better PAE, or (b) report no feasible solution at the current device geometry — at which point the diagnosis flips from "optimizer stuck" to "Stage-1 needs to pick a different device."

R7 — CMA-ES replaces Nelder-Mead

File: 3085 lines · 147 KB.

Constrained 5-D optimizer swapped from Nelder-Mead to (μ/μw, λ)-CMA-ES per Hansen 2016. Three new helpers added near nelderMeadAsync: eigSym (Jacobi diagonalization for symmetric matrices), randn (Box-Muller standard normal), and cmaESAsync (full CMA-ES with σ-evolution path, C-evolution path, rank-1 + rank-μ updates, h_σ-corrected covariance compensation).

runPAEOptimization rewritten in three places. First, search now operates in bound-normalized [0,1]⁵ coordinates via normalize/denormalize helpers — necessary because the variables span 1e-12 (pF) to 1e-9 (nH) and σ has no uniform meaning otherwise. Second, local.best is now tracked inside the cost function itself, not just onIter, because CMA-ES evaluates λ=8 candidates per generation but only the best-of-generation reaches onIter; tracking inside cost captures good points from anywhere in the population. Third, convergence is checked against local.best rather than the current vertex, since CMA-ES explores aggressively and any single generation's best may be infeasible while local.best is solid.

Default settings: λ=8, σ₀=0.3 (of normalized range), maxIter=80 generations. Wall time comparable to the previous NM-400 since CMA-ES does ~640 evaluations vs. NM's 600–1200. UI panel description updated to "CMA-ES, λ=8, ~80 generations." Console breadcrumb relabeled "(CMA-ES)."

nelderMeadAsync is retained in the file for reference and for easy reversion (one-line change at the call site). Not deleted because the algorithmic context is useful documentation.

R6 — Show local.best in readout

File: 2896 lines.

The per-iter live readout was sourcing from paeOpt.current — the last NM vertex visited, often clamped. The right-panel "ACTIVE NETWORKS" card was sourcing from local.best — the best non-degenerate point ever seen. Numbers disagreed: one run showed iter 400 readout PAE=27.1% with BVdss CLAMPED while ACTIVE NETWORKS showed PAE=37.2% clean.

Three small changes resolved the mismatch: local.best now stamps foundAtIter: iter when set; the readout JSX sources from paeOpt.best || paeOpt.current (best preferred, current as early-iter fallback); and the iter label gains (best @ N) annotation when displayed metrics aren't from the current vertex. End result: readout PAE/S22/S11 match the right panel.

R5 — Coupled-optimizer chain bug fixes

File: 2886 lines.

Coupled run was completing Stage 1 and applying device geometry to sliders, but never executing Stage 2. Two bugs:

The Stage-2 launcher used setTimeout(() => runPAEOptimization(), 100) and depended on runPAEOptimization in its effect dep array. When the effect set coupledOpt.pending = false and triggered a re-render, the cleanup function () => clearTimeout(t) killed the pending setTimeout before it could fire. Stage 2 was silently aborted on every coupled run.

Separately, the chain-finish effect checked coupledOpt.phase === "stage2_running" && !paeOpt.running. When phase first becomes "stage2_running", paeOpt.running is still false (Stage 2 hasn't actually started yet), so chain-finish fired immediately and flipped phase to "stage2_done" before Stage 2 ever ran.

Fixes: (1) merged the two effects (auto-apply + Stage-2 launcher) into one with explicit two-branch state machine; (2) replaced setTimeout with Promise.resolve().then(launch) — microtasks aren't tied to effect cleanup; (3) captured runPAEOptimization in a const launch = snapshot inside the effect; (4) chain-finish now uses a useRef transition detector that fires only on paeOpt.running true→false transition, not on any "currently false" reading.

useRef added to React imports.

R4 — Coupled (two-stage) optimizer

File: 2868 lines.

New "Run Coupled Optimization" button chains brute-force device sizing and constrained matching design. Stage 1 runs the existing brute-force optimizer to find optimal (W, Vdd, Vgs_q) under the analytic L-match networks. Stage 2 runs the constrained 5-D optimizer to redesign OMN+IMN around that device.

Architecture uses three useEffect hooks rather than a single async callback. The brute-force optimizer commits its result via setTimeout(50) and setOptimResult, and the constrained optimizer needs slider state to reflect the new device before it reads physics.ropt. Threading that through one callback would require awaiting React state, which doesn't work. Effects are the React-idiomatic solution.

New state: coupledOpt = { pending, phase } where phase ∈ idle | stage1_running | stage2_running | stage2_converged | stage2_done. Effect 1 auto-applies Stage-1 result to sliders when pending. Effect 2 launches Stage 2 when sliders match the brute-force result. Effect 3 marks chain finished when Stage 2's paeOpt.running goes false.

Button placement: top of the constrained-optimizer panel, orange→cyan gradient distinguishing it from the standalone match-only button (cyan→blue). Phase text updates live on the button.

(Bugs in this implementation discovered and fixed in R5.)

R3 — Robust degenerate-evaluation handling

File: 2774 lines.

Coupled run reported "Stopped at iter 400" but never committed omnOverride or imnOverride — the if (e.metrics) guard in onDone was false because evalCandidate returned { J: 1e5, metrics: null } whenever HB clamped against BVdss or the solver failed.

The constrained optimizer was trading port-match for power-match aggressively enough to drive the device into BVdss territory at the final NM vertex, so null metrics happened on the very point being committed.

Fixed by making evalCandidate always return metrics. Hard rejects (HB failed, BVdss clamped) now feed soft penalties into J — penHB = 1e4, penClamp = 200 (later quadratic in R8) — and metrics carry clamped / hbFailed flags. The optimizer can navigate out of clamping regions instead of being blind to them. onDone always commits unless every single evaluation was degenerate, falling back to local.best.x if the final vertex is degenerate. onIter only displaces local.best with non-degenerate points.

Console breadcrumb added so a real run is auditable in DevTools — logs committed component values and run summary on completion. Per-iter readout shows "BVdss CLAMPED" or "HB FAILED" status when those conditions trigger.

R2 — Extension to 5-D (OMN + IMN)

File: 2732 lines.

Constrained optimizer extended from 3-D (OMN only) to 5-D (3 OMN + 2 IMN). S11 cannot be controlled from the OMN alone — reverse coupling through S12 is ~−57 dB, too weak. With OMN-only, runs stalled at S11 ≈ −7.9 dB regardless of band weight escalation. Adding C_in (input shunt-C) and L_g (gate series-L) as free variables resolves this.

buildCascodeNetwork accepts new imnOverride parameter; input match branches on it. solveCascodeSParams threads imnOverride through. New helpers: IMN_BOUNDS (C_in ∈ [0.5, 5] pF, L_g ∈ [0.5, 10] nH), ALL_5D_BOUNDS, clip5D. Live S-param paths (f₀, band sweep, 1–6 GHz stability) all pass imnOverride. physics useMemo deps include imnOverride.

runPAEOptimization rewritten 3-D → 5-D: new IMN seeds derived from same formulas the legacy input network uses, new bounds, new evalCandidate signature, S11 band-max added to convergence test. clearOmnOverride resets both overrides. UI panel updated to show C_in / L_g and S11 band-max in the per-iter readout.

R1 — Constrained PAE optimizer (initial)

File: 2679 lines · 144 KB · +435/−19 from baseline.

First implementation of the constrained PAE optimizer. Topology change: legacy 2-element high-pass L-section (series-C, shunt-L at drain) replaced with 3-element band-pass π OMN (shunt-L on drain serving as DC feed, series-C, shunt-C on port). The L-section's failure mode at the original W=2000/Vdd=3.3 device was exactly the textbook scenario for the topology — Q≈3 real-to-real match driven from a much-higher-than-design source impedance, with port impedance dominated by the shunt-L reactance giving |S22| ≈ −1 dB.

New helpers near the main component: omnPiPortImpedance, omnPiZHarm (drain-side input impedance of the π OMN with 50 Ω termination, used to derive harmonic loads for the existing HB solver), OMN_PI_BOUNDS, clipOmnPi, nelderMeadAsync (generic async Nelder-Mead with multistart-friendly API).

buildCascodeNetwork accepts optional omnOverride parameter. solveCascodeSParams threads it through. Live S-param paths and z_harm_main for HB switch to π topology when override is committed. physics useMemo deps include omnOverride.

runPAEOptimization callback added. Maximizes device PAE subject to soft penalties on |S22| < −12 dB at f₀, band-max |S22| < −12 dB across n78, |S11| < −10 dB at f₀, K > 1.05. NM with 200 max iters, stepFrac=0.15. UI panel with "Run Constrained PAE Optimization" button, per-iter live readout, ACTIVE NETWORKS display, reset button.

The HB solver itself remained untouched; only its harmonic load impedances change when override is committed.


Outstanding limitations / possible follow-ups

A few things came up during the iteration that weren't addressed and may matter depending on direction.

Path B not synced. The user-selectable matchStages dropdown / netlistMultiL / netlistPi library is independent of Path A's optimizer-driven OMN. After an optimizer commit, the matching panel still shows the library netlist's matching loss as informational, while the live S-params reflect the optimizer's π OMN. If exact agreement is needed, Path B would need to synthesize an equivalent multiL netlist from the optimized (L_d, C_s, C_o) values.

Single-section topology limit. Constrained runs consistently fail S22 band-max < −12 dB and S11 band-max < −10 dB at the n78 edges (3.40 and 3.70 GHz). Center frequency is fine; edges are 1–3 dB worse than spec. The natural −12 dB bandwidth of a Q≈3 single-section π is ~250 MHz, narrower than n78's 300 MHz. Two paths to fix: two-section ladder OMN/IMN (adds ~2 more variables, code change in buildCascodeNetwork), or relax the band constraint to ±100 MHz around f₀.

Multistart for CMA-ES. Currently single-start from the analytic L-match seeds. CMA-ES is robust but a 5–10-start Latin hypercube would provide cheap insurance for users who want a guaranteed global search.

runPAEOptimization deps. Includes physics.ropt, which means the callback identity changes whenever any physics input updates. Inside coupled runs this is fine because R5 captures the callback in a snapshot. For users binding keyboard shortcuts or programmatic re-runs, worth knowing.

Brute-force optimizer untouched. The user said "keep brute-force results" multiple times so I never modified it. It still uses the analytic L-section internally for system-PAE scoring; if the L-section's S22 is poor on the chosen device geometry, the brute-force will still rank that geometry highly. R4 onward addresses this by chaining: brute-force picks the device, then the constrained optimizer redesigns the network for it. But the brute-force itself doesn't see Path A's S22.

A note on what's outside this changelog

I did not modify (and have not had cause to modify) the existing brute-force optimizer (runOptimization), the harmonic-balance solver (solvePAMultiHarmonic), the Angelov device model, the self-heating loop, the Path B matching netlist library (netlistMultiL / netlistPi), the existing UI tabs for S-params / stability / band sweeps, the n78 band definition, or any of the supporting plot components. All revisions touched only the linear MNA's OMN/IMN sections, the new constrained optimizer code path, and the UI panel for that optimizer.

Content is user-generated and unverified.
    Constrained PAE Optimizer Changelog – R1–R8 Revisions | Claude