# Prompt — Rich parameter help (click-to-open explainers with diagrams) Upgrade the "?" help for the 8 Parameters in the Lab from a one-line tooltip into a rich, click-to-open explainer (text + examples + diagrams), aimed at a non-expert reviewer. Presentation/read-only — no logic changes. Reuse the calm theme (bg #FAFAF7, card #FCFBF8, ink #23303A, muted #6E7F8C, border #ECEAE4, accent #3A6B7E, highlight #BC6B2E) and the existing monospace for gene IDs. ## Interaction - Build ONE reusable component `` so wording can't drift. The "?" button: - on **hover/focus**, shows the SHORT one-liner (a small tooltip — keep the current text as the short summary); - on **click**, opens a MODAL dialog with the DETAILED content (text + any diagram). - Modal: `role="dialog"`, `aria-modal="true"`, labelled by its title; ~640px wide on desktop, full-width with padding and vertical scroll on narrow screens; close on X, Esc, and backdrop click; return focus to the "?" button on close. Calm card styling (border #ECEAE4, fill #FCFBF8, ~14px radius, generous padding). Headings in #23303A, body readable (~14px), muted captions #6E7F8C. - Apply this ONLY to the 8 Parameters "?" icons. Leave other "?" icons elsewhere (objectives, run id/status) as the existing InfoTip — do not touch them. - Diagrams are embedded as small SVG components, `width:100%; height:auto` (viewBox scales). Each diagram is used once, in its own parameter. ## Detailed content per parameter For each: SHORT = hover text; DETAILED = modal body (use the wording below; render line breaks and bullet/example blocks cleanly). ### Generations SHORT: "How many rounds of 'keep the best, breed, mutate' the engine runs. More rounds refine further, but take longer." DETAILED: - What it is: one "generation" is a single round of the engine's loop — score every program, keep the best, then breed and mutate them into the next batch. Generations is how many rounds it runs. - How it works: the engine starts with random programs (mostly poor) and each round nudges the population toward better ones — like selective breeding. - More vs fewer: more rounds refine the winners further, but with diminishing returns once it has converged. The harder part is usually *discovering* the right genes in the first place (driven by Population and mutation), not polishing — so extra generations help less once the curve flattens. - Typical: 30–60. - Small inline illustration: a 4-step loop "random programs → score each → keep the best → breed & mutate → (back to score)", calm style, arrows; the loop arrow labelled "× Generations". ### Population SHORT: "How many candidate programs compete in each round. A 'program' is a small pipeline built by composing the DSL operators (Select → Reduce → Fit) over the gene data. More programs explores more options, but is slower." DETAILED: - What it is: how many candidate programs compete each round. A "program" is a small pipeline built from the DSL operators — Select (pick genes) → Reduce (average them into one score) → Fit (use the score to separate the groups). - Why it matters: a bigger population samples more genes and structures each round, so it's the main lever for *discovery* — finding the right genes at all. Generations then refine what was found. - Example — three random programs in a population (opaque gene IDs, monospace): - `average(g04823, g11201, g00917) → score` - `average(g15522, g02013) − average(g08840, g00231) → score` - `average(g07788, g13002, g05340) → score` - More vs fewer: more explores more options, but each round is slower. - Typical: 150–300. ### Genes / set SHORT: "The most genes a single score may use. Smaller keeps programs simple and readable." DETAILED: - What it is: a "set" is the group of genes averaged together into a single score. Genes/set is the maximum size of that group. - Example: `Select(g05347, g00048, g06271) → average → one score` (uses 3; the cap might be 8). - Smaller vs larger: smaller keeps each score simple and readable; larger lets one score blend more genes. - It pairs with Max sets: Genes/set is the *width* of each group; Max sets is *how many* groups. ### Max sets SHORT: "How many separate gene-scores a program may combine (1 or 2). 2 lets it build a small 'score made of scores'." DETAILED: - What it is: how many *separate* gene groups a program may build and then combine (1 or 2). They use different genes — not the same ones reused. - Example (2 sets): - Set 1: `average(g05347, g00048, g06271) → scoreA` - Set 2: `average(g15522, g02013) → scoreB` - Combine: `scoreA − scoreB → final score` - Why two: to capture a *contrast* — e.g. one group that's low in MSI-H and one that's high; the gap between them can separate better than either alone (like "repair activity minus immune activity"). One set can't express that; two can. - If one group is enough, the engine can still use 1. - Diagram (small): two short chains — `matrix → Select → Reduce → scoreA` and `matrix → Select → Reduce → scoreB` — both feeding a `Combine (sub)` node, then a final `score`. Verbs = accent-bordered boxes, scores = amber pills, calm style. ### λ (size penalty) SHORT: "How hard the engine is penalised for using more genes. Higher pushes it toward fewer genes (simpler answers)." DETAILED: - What it is: a "price per gene" that discourages bloated programs. Every program is graded on a single number: - `fitness = separation − λ × (number of genes)` - so the engine ranks by accuracy *minus* a size tax. - The key idea — λ is the admission price each gene must beat: with λ = 0.005, a gene is only worth keeping if it adds more than 0.005 of separation. - Worked example: a 3-gene program scores 0.90 → net 0.90 − 0.015 = 0.885. Add a 4th gene that lifts it to 0.903 (only +0.003) → net 0.883, *lower*, so it's rejected. A 4th gene that lifts it to 0.91 (+0.01) → net 0.890, *higher*, so it's kept. - Turning the knob: λ = 0 → genes free → bloated, overfit programs. λ small (0.005) → trims useless genes, keeps useful ones. λ large → very lean programs, but may drop useful genes. - Diagram: the accuracy-vs-genes trade-off (embed the SVG in "DIAGRAMS" below). ### Seed SHORT: "The starting point for the engine's randomness. The same seed reproduces the exact same run; change it to see a different run." DETAILED: - Why randomness: the space of possible programs is astronomically large (picking even 8 genes out of 20,000 is ~10^29 combinations), so the engine can't try them all — it explores with randomness: random starting programs, random mutations, random breeding. - What the seed is: computers don't make true randomness; a formula generates a sequence of numbers, each from the previous one. The seed is the *starting number* fed into that formula — everything random flows from it. (One common formula: `next = (1664525 × current + 1013904223) mod 2³²`; with seed 42 the first value is 1,083,814,273, then mapped onto a gene position.) - What it does: think of the seed as one fixed list of dice rolls used in order across the whole run — early rolls pick the starting genes, later rolls drive mutations. Same seed → same list → identical run. Different seed → a different list → a different run. - Why vary it: because the search has luck in it, run a few seeds (42, 7, 123) and see whether the same genes keep appearing — that tells you a finding is real, not a fluke. - Diagram: the seed-as-a-fixed-list-of-dice-rolls (embed the SVG below). ### Permutations SHORT: "How many times we re-run on deliberately scrambled labels to check the result isn't luck. The real result must beat these chance runs (that's the 'permutation p')." DETAILED: - What it checks: whether the winning score is real signal or could have come up by luck. The engine searches so hard that some programs separate the groups well by pure coincidence, so a high score alone isn't proof. - How it works: shuffle the MSI-H / MSS labels across patients at random — this breaks any real gene↔label link, creating a "no-signal world". Re-score there: any score above 0.5 is pure chance. Repeat many times (this knob = how many) to build a picture of what luck looks like. - Reading it: permutation p = the fraction of shuffled runs that scored ≥ the real winner. p = 0.005 means only 0.5% of chance runs matched it → very unlikely a fluke. Conventionally p < 0.05 is the "unlikely to be luck" line. - The knob: with 200 shuffles the smallest p you can report is about 1/200 ≈ 0.005; more permutations give a finer, more trustworthy p. - Diagram: the "luck pile" vs the real result (embed the SVG below). ### Prefilter top-N SHORT: "By default the engine searches all ~20,000 genes, so nothing is pre-excluded. Turning this on narrows to the N most promising genes first — faster, but it can drop a real gene that only shows signal in combination." DETAILED: - Default (off): the engine searches all ~20,000 genes, so nothing is pre-excluded — the most honest setting for a discovery demo. - Turning it on: first narrows to the N genes most individually associated with the target (computed name-blind, on the training split only), then searches within that shortlist. Faster, because the space is ~10× smaller. - The trade-off: speed and focus vs completeness. A univariate shortlist can drop a gene that only matters *in combination* (no signal on its own), and it sets a ceiling — if a gene isn't in the shortlist, the engine can never find it. - Small inline illustration (funnel): "~20,000 genes → keep the N most individually associated → search within N", with a muted note "off by default = no funnel". ## DIAGRAMS (embed verbatim; wrap each so it scales: width:100%, height auto) ### Seed diagram ```svg The seed as one fixed list of dice rolls used in order across the whole run Seed 42 produces a fixed sequence of dice rolls; early rolls pick initial genes, later rolls drive a mutation. Same seed gives the same run. Seed 42 → one fixed list of random rolls, used in order: Generation 0 — pick the initial programs' genes Generation 5 — a mutation … many rolls later … Same seed → same list → same run. Change the seed → a different list → a different run. ``` ### λ trade-off diagram ```svg How lambda picks the best program size: net fitness = separation minus a per-gene penalty Raw separation rises then plateaus; net fitness subtracts lambda times genes and peaks at an intermediate size; a larger lambda moves the peak to fewer genes. 0.50.60.70.80.9 02468101214 number of genes in the program separation (accuracy) raw separation rises fast, then plateaus — extra genes barely help peak (λ = 0.005) engine keeps ~7 genes higher λ = 0.03 peak shifts left → ~4 genes raw separation net fitness (λ=0.005) net fitness (λ=0.03) ``` ### Permutations diagram ```svg Permutation null: scores from 200 shuffled-label runs versus the real result A histogram of 200 shuffled-label scores clusters near 0.58, well below the real winning score of 0.89; no shuffle reaches it, giving a permutation p of about 0.005. 0.40.50.60.70.80.91.0 score (separation / AUROC) number of shuffled runs 0.5 = guessing 200 shuffled-label runs (chance) if there were no real signal, scores land in here no shuffle ever gets this far → real result: 0.89 p ≈ 0.005 ``` ## "How these parameters relate" chart - Keep the neat (HTML/flex, wrapping) version already built. - Enhancement: make each parameter NAME in the chart's pills a button that opens that parameter's ParamHelp modal (same content). So the chart becomes a map into the detailed help. ## Constraints - Presentation-only; do not change any GP logic or other tooltips. - One reusable `ParamHelp` component + one reusable modal; diagrams are separate small components used once each. - Accessible: "?" is a real button (keyboard-activatable); modal has role/aria, Esc + backdrop + X close, focus trap and focus return. - Responsive: modal scrolls on small screens; diagrams scale to container width. - These are Client Components ("use client") since they manage open/close state.