// An answer in the answer view layer (wai/extensions/quantum-answers.md), drawn as // it arrives: the root's layout as soon as its line does, each view once its // statement is ready, its numbers from its engine call carried out here, a program's // circuit gate by gate while its lines stream in, and every problem with its code. // // The text goes through the engine's own reader (engine.answer('snapshot'), then // 'parse' once it has ended), so what is drawn is what the layer says; a view // whose call the engine refuses shows the refusal, for that view alone. A seal // is checked here, by the engine, and the answer can be kept with its receipt. import { el, sameness } from './dom.js'; import { circuitSvg } from './circuit-svg.js'; import { verdictLine } from './verdict-view.js'; import { chips, fidelityChip } from './critique-view.js'; import { frontierSvg, compact as count, duration } from './estimate-view.js'; import { blochSphere } from './state-views.js'; import { METHOD_NAMES, countsBlock, expectationsBlock, amplitudesBlock } from './result-parts.js'; const note = (text, attrs = {}) => el('p', { class: 'qs-note', ...attrs, text }); // A view's card: its name and view, then what it shows. function card(name, what) { const body = el('div', { class: 'qs-av-body' }, note('waiting for its statement…')); const node = el('div', { class: 'qs-av-card', 'data-name': name, 'data-state': 'waiting' }, el('div', { class: 'qs-k' }, el('span', { text: name }), el('span', { class: 'qs-av-what', text: what ? ` · ${what}` : '' })), body); return { node, body }; } // The program a fence has opened and not yet closed, as far as it has come. function openProgram(text) { const lines = text.split('\n'); let open = null; for (let i = 0; i < lines.length - 1; i++) { const t = lines[i].trim(); if (open && t === '```') open = null; else if (open) open.lines.push(lines[i]); else { const m = /^```(?:qasm|openqasm)\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/.exec(t); if (m) open = { name: m[1], lines: [] }; } } return open && open.lines.length ? { name: open.name, qasm: open.lines.join('\n') + '\n' } : null; } // What a statement's value shows, by its view. function draw(view, v, engine) { if (!v || v.ok === false) { return [el('p', { class: 'qs-warn', text: v?.error || 'the engine gave no value' }), v?.code ? note(`${v.code} · ${v.outcome}: ${v.meaning || ''}`, { class: 'qs-note qs-av-code' }) : null]; } const x = v.value; switch (view) { case 'Text': return [el('p', { class: 'qs-why', text: x.text ?? '' })]; case 'Circuit': case 'program': { if (!x.layout) return [note(x.why || 'not drawn')]; const { svg } = circuitSvg(x.layout); return [el('div', { class: 'qs-av-circuit' }, svg), x.description ? el('details', {}, el('summary', { text: 'The circuit in words' }), el('p', { class: 'qs-why', text: x.description })) : null]; } case 'Histogram': return [note(`${METHOD_NAMES[x.method] || x.method}: ${x.why || ''}`), countsBlock(x.result)]; case 'Expectation': return [expectationsBlock(x.result)]; case 'Amplitudes': return [amplitudesBlock(x.result)]; case 'Bloch': { const b = x.bloch || {}; return [el('div', { class: 'qs-av-bloch' }, blochSphere(b, 120), el('p', { class: 'qs-why', text: `qubit ${b.qubit}: (${Number(b.x).toFixed(4)}, ${Number(b.y).toFixed(4)}, ${Number(b.z).toFixed(4)}), length ${Number(b.length).toFixed(4)}, purity ${Number(b.purity).toFixed(4)}` }))]; } case 'Verdict': return [verdictLine(x, { sealed: false })]; case 'Equivalence': { const s = sameness(x); return [el('span', { class: `qs-badge ${s.cls}`, text: s.text }), s.why ? el('span', { class: 'qs-why', text: ` ${s.why}` }) : null]; } case 'Critique': return [el('div', { class: 'qs-chips' }, ...chips(x), fidelityChip(x)), el('ul', { class: 'qs-smells' }, ...(x.smells || []).map((s) => el('li', { class: 'qs-smell' }, el('span', { class: 'qs-smell-text', text: s.message }))))]; case 'Estimate': { const e = x.estimate || {}; const pts = x.frontier || []; return [el('p', { class: 'qs-why', text: `${count(e.physical_qubits)} physical qubits, ${e.factories} factories, ${duration(e.runtime_s)} at code distance ${e.distance}` }), pts.length ? el('div', { class: 'qs-frontier-figure' }, frontierSvg(pts, Math.max(0, pts.findIndex((p) => p.headline)), () => {})) : null]; } case 'Compile': { const c = x.certificate || {}; return [el('p', { class: 'qs-why', text: `${c.verdict}: ${c.output?.counts?.gates} gates, ${c.output?.counts?.t_count} T, within ${c.epsilon?.bound} of the program (budget ${c.epsilon?.budget})` })]; } case 'Plan': return [el('p', {}, el('b', { text: METHOD_NAMES[x.method] || x.method || '' }), el('span', { class: 'qs-why', text: ` ${x.why || ''}` }))]; case 'Receipt': { const check = el('span', { class: 'qs-badge', 'data-receipt': 'checking', text: 'checking here…' }); engine.verify(x).then((r) => { check.dataset.receipt = r.valid ? 'valid' : 'invalid'; check.className = `qs-badge ${r.valid ? 'ok' : 'bad'}`; check.textContent = r.valid ? `✓ seals ${(x.request?.sealed || []).join(', ')}, checked here` : '✗ does not verify'; }).catch((e) => { check.dataset.receipt = 'unchecked'; check.textContent = `not checked: ${e.message}`; }); return [check, note(`signed by ${(x.result?.receipt?.key || '').slice(0, 16)}…; the receipt shows that this key sealed these values for this answer, not who holds the key.`)]; } default: return [note(JSON.stringify(x).slice(0, 400))]; } } // A renderer: `feed(text)` as the answer arrives, `end()` once it has ended. `engine` // answers answer(...), layout(...) and verify(...); `onEnded(summary)` hears the // finished answer: its parse, each view's value, and the seal's. export function answerRenderer({ engine, onEnded = () => {} }) { const shell = el('div', { class: 'qs-av-shell', id: 'qs-av-shell' }); const live = el('div', { class: 'qs-av-live', id: 'qs-av-live' }); const diags = el('ul', { class: 'qs-av-diags', id: 'qs-av-diags' }); const node = el('div', { class: 'qs-av', id: 'qs-av' }, shell, live, diags); let text = ''; let cards = new Map(); let drawn = new Set(); let values = new Map(); let queue = Promise.resolve(); let lines = 0; const cardFor = (name, what) => { if (!cards.has(name)) { const c = card(name, what); cards.set(name, c); shell.append(c.node); } return cards.get(name); }; async function refresh(complete) { const a = await engine.answer(complete ? 'parse' : 'snapshot', { text }); if (a.error) { diags.replaceChildren(el('li', { class: 'qs-warn', text: a.error })); return a; } node.dataset.root = a.root ?? ''; const by = new Map(a.statements.map((s) => [s.name, s])); // The root's layout first: a card for each view it lists, waiting until it is ready. for (const name of a.order) { const s = by.get(name); const c = cardFor(name, s?.view ?? (s?.kind === 'program' ? 'program' : null)); if (s?.view) c.node.querySelector('.qs-av-what').textContent = ` · ${s.view}`; } // Each view and program that is ready, drawn once, its call carried out here. for (const name of a.order) { const s = by.get(name); if (!s || drawn.has(name)) continue; const c = cardFor(name); if (s.status === 'dropped') { c.node.dataset.state = 'dropped'; c.body.replaceChildren(note('left out: see the problems below')); drawn.add(name); continue; } if (s.status !== 'ready') { c.node.dataset.state = 'pending'; c.body.replaceChildren(note(`waiting for ${s.deps.filter((d) => by.get(d)?.status !== 'ready').join(', ') || 'what it names'}…`)); continue; } drawn.add(name); c.node.dataset.state = 'running'; c.body.replaceChildren(note('running on the engine here…')); const view = s.kind === 'program' ? 'program' : s.view; const v = s.kind === 'program' ? await engine.layout(s.qasm).then((l) => (l.error ? { ok: false, error: l.error } : { ok: true, value: { layout: l } }), (e) => ({ ok: false, error: e.message })) : await engine.answer('value', { text, name, complete }); values.set(name, v); c.body.replaceChildren(...draw(view, v, engine).filter(Boolean)); c.node.dataset.state = v.ok === false ? 'refused' : 'drawn'; c.node.dataset.view = view; } // A program still arriving: its circuit, gate by gate. const open = complete ? null : openProgram(text); if (open) { const l = await engine.layout(open.qasm).catch(() => null); if (l && !l.error) live.replaceChildren(el('div', { class: 'qs-k', text: `${open.name}, arriving` }), circuitSvg(l).svg); } else live.replaceChildren(); // The problems: each with its line and code; the errors are what a model is sent back. diags.replaceChildren(...a.diagnostics.map((d) => el('li', { class: `qs-av-diag ${d.severity}`, 'data-code': d.code, 'data-severity': d.severity }, el('span', { class: 'qs-badge' + (d.severity === 'error' ? ' bad' : ''), text: d.code }), ` line ${d.line}: ${d.message}`))); return a; } return { node, // The next piece of the answer; the view redraws as each line completes. feed(piece) { text += piece; const now = text.split('\n').length - 1; if (now === lines) return queue; lines = now; queue = queue.then(() => refresh(false)).catch(() => {}); return queue; }, // The answer has ended: read whole, everything left drawn or left out. end() { queue = queue.then(async () => { const a = await refresh(true); for (const [name, c] of cards) if (!drawn.has(name)) { c.node.dataset.state = 'dropped'; c.body.replaceChildren(note('never arrived')); } node.dataset.done = 'true'; onEnded({ answer: a, text, values }); return a; }); return queue; }, reset() { text = ''; lines = 0; cards = new Map(); drawn = new Set(); values = new Map(); shell.replaceChildren(); live.replaceChildren(); diags.replaceChildren(); delete node.dataset.done; }, text: () => text, }; } // What a finished answer gives a correctness record: its codes (every // diagnostic's, and each refused view's), the first verdict it shows, its first // program, the intent of its first check, and its seal's digest. export function answerRecord({ answer, values }) { const codes = [...answer.diagnostics.map((d) => d.code)]; for (const v of values.values()) for (const c of v?.codes || []) if (!codes.includes(c)) codes.push(c); const stmts = answer.statements || []; const verdictOf = stmts.find((s) => s.view === 'Verdict' && values.get(s.name)?.ok); const program = stmts.find((s) => s.kind === 'program' && s.reads); const seal = stmts.find((s) => s.view === 'Receipt' && values.get(s.name)?.ok); const fields = { codes, program: program?.qasm, receipt: seal ? values.get(seal.name).value?.result?.receipt?.digest : undefined }; if (verdictOf) { const v = values.get(verdictOf.name).value; fields.verdict = { level: v.level, holds: v.holds }; // The intent its check held the program to, as a job's intent. const call = verdictOf.expr?.args?.[0]; const intent = call?.call === 'Check' ? call.args?.[1] : null; if (intent?.object) { const entries = intent.object; const ref = (e) => stmts.find((s) => s.name === e?.ref)?.qasm; if (entries.length === 1 && ['equivalent', 'prepares'].includes(entries[0][0]) && entries[0][1].ref) fields.intent = { [entries[0][0]]: ref(entries[0][1]) }; else fields.intent = { distribution: Object.fromEntries(entries.map(([k, e]) => [k, e.value])) }; } else if (intent?.ref) fields.intent = { equivalent: stmts.find((s) => s.name === intent.ref)?.qasm }; } return fields; }