-
Notifications
You must be signed in to change notification settings - Fork 1
feat(quantum): Q-Share Phase D — P2P sharing notebook + web Results/Backends tabs #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| { | ||
| "nbformat": 4, | ||
| "nbformat_minor": 5, | ||
| "metadata": { | ||
| "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, | ||
| "colab": {"name": "knitweb-lens: P2P sharing of circuits, results & systems", "provenance": []} | ||
| }, | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": ["# P2P sharing of circuits, **results** & **quantum systems**\n", | ||
| "\n", | ||
| "The quantum layer shares three content-addressed artifact kinds, each in its own CID namespace:\n", | ||
| "\n", | ||
| "| kind | type | CID |\n", | ||
| "|---|---|---|\n", | ||
| "| circuit | `QuantumCircuit` | `lcid:` |\n", | ||
| "| result | `QuantumResult` | `lres:` |\n", | ||
| "| system | `BackendDescriptor` | `lqpu:` |\n", | ||
| "\n", | ||
| "This notebook runs a circuit, captures its **result**, describes the **backend** it ran on, then shares all three between two nodes by content id — like file-sharing, but for quantum.\n", | ||
| "\n", | ||
| "[](https://colab.research.google.com/github/Knitweb/lens/blob/main/notebooks/05_p2p_fabric_sharing.ipynb)\n"] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": ["!pip install -q knitweb-lens qiskit qiskit-aer"] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": ["## 1. A circuit → its `lcid:`"] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from knitweb_lens.quantum import library, QuantumResult, BackendDescriptor, Store, search\n", | ||
| "\n", | ||
| "circ = library()['bell_phi_plus']\n", | ||
| "print('circuit :', circ.meta.name)\n", | ||
| "print('cid :', circ.cid) # lcid:...\n", | ||
| "print(circ.qasm)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": ["## 2. Run it → a `QuantumResult` (`lres:`)\n", | ||
| "We execute on Qiskit Aer and wrap the measured counts as a content-addressed result that links back to the circuit."] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from qiskit import qasm2, transpile\n", | ||
| "from qiskit_aer import AerSimulator\n", | ||
| "\n", | ||
| "sim = AerSimulator()\n", | ||
| "qc = qasm2.loads(circ.qasm)\n", | ||
| "counts = sim.run(transpile(qc, sim), shots=1024).result().get_counts()\n", | ||
| "counts = {k.replace(' ', ''): int(v) for k, v in counts.items()}\n", | ||
| "print('counts:', counts)\n", | ||
| "\n", | ||
| "result = QuantumResult(circuit_cid=circ.cid, counts=counts, author='demo')\n", | ||
| "print('result cid :', result.cid) # lres:...\n", | ||
| "print('of circuit :', result.circuit_cid)\n", | ||
| "print('most freq :', result.most_frequent)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": ["## 3. Describe the backend → a `BackendDescriptor` (`lqpu:`)"] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "backend = BackendDescriptor(name='aer_simulator', provider='local', n_qubits=32,\n", | ||
| " native_gates=['h', 'cx', 'rz', 'ry', 'rx', 'ccx'], simulator=True)\n", | ||
| "print('backend cid:', backend.cid) # lqpu:...\n", | ||
| "\n", | ||
| "# Bind the result to the system it ran on (provenance).\n", | ||
| "result = QuantumResult(circuit_cid=circ.cid, counts=counts,\n", | ||
| " backend_cid=backend.cid, author='demo')\n", | ||
| "print('result now records ran-on:', result.backend_cid)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": ["## 4. Node **A** publishes all three by content id\n", | ||
| "Set `KNITWEB_RELAY` to a live Knitweb relay to actually broadcast; otherwise this writes to a local node store and we simulate the peer sync in the next cell."] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import os, tempfile\n", | ||
| "relay = os.environ.get('KNITWEB_RELAY') # e.g. 'https://relay.5mart.ml'\n", | ||
| "node_a = Store(root=tempfile.mkdtemp(prefix='nodeA_'), relay=relay)\n", | ||
| "\n", | ||
| "for art in (circ, backend, result):\n", | ||
| " cid = node_a.put(art)\n", | ||
| " print('published', cid)\n", | ||
| "\n", | ||
| "print('\\nnode A holds', len(node_a), 'artifacts across kinds:')\n", | ||
| "for item in node_a.list():\n", | ||
| " print(' ', item['kind'], item['cid'])" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": ["## 5. Node **B** pulls by CID — knowledge crosses the network\n", | ||
| "With a relay, node B fetches from it. Without one, we hand B the same store dir to stand in for a completed peer sync — the point is that **the CID is all B needs**."] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "node_b = Store(root=(tempfile.mkdtemp(prefix='nodeB_') if relay else node_a.root), relay=relay)\n", | ||
| "\n", | ||
| "got_circuit = node_b.get(circ.cid)\n", | ||
| "got_result = node_b.get(result.cid)\n", | ||
| "got_backend = node_b.get(backend.cid)\n", | ||
| "\n", | ||
| "print('B pulled circuit:', got_circuit.meta.name)\n", | ||
| "print('B pulled result :', got_result.most_frequent, 'from', got_result.circuit_cid)\n", | ||
| "print('B pulled backend:', got_backend.name, f'({got_backend.n_qubits}q)')\n", | ||
| "\n", | ||
| "# Content integrity: the CID B fetched recomputes to the same id A minted.\n", | ||
| "assert got_circuit.cid == circ.cid\n", | ||
| "assert got_result.cid == result.cid\n", | ||
| "assert got_backend.cid == backend.cid\n", | ||
| "print('\\nAll CIDs verified — bit-identical across nodes.')" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": ["## 6. Query across kinds on node B"] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "print('results on B :', [i['cid'] for i in node_b.list(kind='result')])\n", | ||
| "print('backends on B:', [i['cid'] for i in node_b.list(kind='backend')])\n", | ||
| "print('find \"aer\" :', [h['meta']['name'] for h in node_b.find(query='aer', kind='backend')])" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": ["## 7. True P2P over the Knitweb fabric\n", | ||
| "For real peer-to-peer discovery (anti-entropy sync + a read-only `/interpret` query gateway + a proof-of-useful-work \"run this circuit, return verified counts\" job), a Knitweb **node** weaves these artifacts into the fabric via `knitweb.quantum` (in the [`Knitweb/pulse`](https://github.com/Knitweb/pulse) package):\n", | ||
| "\n", | ||
| "```python\n", | ||
| "from knitweb.quantum import QuantumCircuitRecord, publish, build_lens\n", | ||
| "rec = QuantumCircuitRecord(circuit_cid=circ.cid, name='bell', qubits=2, domain='fundamental')\n", | ||
| "await node.weave(rec.to_record()) # announces the CID to peers\n", | ||
| "# peers can then: query_space(space, 'circuits domain=fundamental') -> [lcid:...]\n", | ||
| "```\n", | ||
| "\n", | ||
| "The `knitweb-lens` SDK mints and stores the artifacts; the fabric carries the lean, queryable descriptors. Same CIDs, everywhere."] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,7 +7,7 @@ | |||||||||
| <style> | ||||||||||
| :root { | ||||||||||
| --bg: #0e1117; --surface: #161b22; --border: #30363d; | ||||||||||
| --text: #e6edf3; --muted: #7d8590; --accent: #58a6ff; | ||||||||||
| --text: #e6edf3; --muted: #7d8590; --accent: #58a6ff; --accent2: #79c0ff; | ||||||||||
| --green: #3fb950; --yellow: #d29922; --purple: #bc8cff; | ||||||||||
| --red: #f85149; --orange: #f0883e; | ||||||||||
| } | ||||||||||
|
|
@@ -75,6 +75,21 @@ | |||||||||
| .colab-card h3{font-size:12px;color:var(--text)} | ||||||||||
| .colab-card p{font-size:11px;color:var(--muted);margin-top:4px} | ||||||||||
| .colab-card a{display:inline-block;margin-top:8px;font-size:11px;color:var(--accent)} | ||||||||||
|
|
||||||||||
| .tabs{max-width:860px;margin:0 auto;padding:6px 24px 0;display:flex;gap:8px} | ||||||||||
| .tab{background:transparent;border:1px solid var(--border);border-bottom:none; | ||||||||||
| border-radius:8px 8px 0 0;color:var(--muted);cursor:pointer;font:inherit;font-size:12px;padding:8px 16px} | ||||||||||
| .tab:hover{color:var(--text)} | ||||||||||
| .tab.active{color:var(--accent);border-color:var(--accent)55;background:var(--accent)0d} | ||||||||||
| .hist{display:flex;flex-direction:column;gap:3px;margin-top:10px} | ||||||||||
| .hist-row{display:flex;align-items:center;gap:8px;font-size:10px} | ||||||||||
| .hist-row .bs{font-family:var(--mono,monospace);color:var(--muted);width:52px;text-align:right} | ||||||||||
| .hist-bar{height:8px;background:var(--accent);border-radius:2px;min-width:2px} | ||||||||||
| .hist-row .n{color:var(--muted);width:44px} | ||||||||||
| .kv{display:grid;grid-template-columns:auto 1fr;gap:2px 10px;margin-top:8px;font-size:11px} | ||||||||||
| .kv .k{color:var(--muted)} .kv .v{color:var(--text);word-break:break-all} | ||||||||||
| .pill-sim{font-size:9px;padding:1px 6px;border-radius:3px;background:var(--green)18;border:1px solid var(--green)44;color:var(--green)} | ||||||||||
| .pill-hw{font-size:9px;padding:1px 6px;border-radius:3px;background:var(--orange)18;border:1px solid var(--orange)44;color:var(--orange)} | ||||||||||
|
Comment on lines
+91
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): Pill background/border colors reuse the same invalid color pattern as the active tab styles. These |
||||||||||
| </style> | ||||||||||
| </head> | ||||||||||
| <body> | ||||||||||
|
|
@@ -120,8 +135,15 @@ <h3>04 Qiskit & PennyLane</h3> | |||||||||
| </div> | ||||||||||
| </div> | ||||||||||
|
|
||||||||||
| <div class="tabs" id="tabs"> | ||||||||||
| <button class="tab active" data-tab="circuits" onclick="setTab('circuits',this)">⬡ Circuits</button> | ||||||||||
| <button class="tab" data-tab="results" onclick="setTab('results',this)">▤ Results</button> | ||||||||||
| <button class="tab" data-tab="backends" onclick="setTab('backends',this)">⚛ Backends</button> | ||||||||||
| </div> | ||||||||||
|
|
||||||||||
| <div class="controls"> | ||||||||||
| <input class="search-input" id="search" placeholder="Search circuits…" oninput="render()"> | ||||||||||
| <span id="domain-row" style="display:contents"> | ||||||||||
| <button class="chip active" data-domain="" onclick="setDomain('',this)">All</button> | ||||||||||
| <button class="chip" data-domain="fundamental" onclick="setDomain('fundamental',this)">fundamental</button> | ||||||||||
| <button class="chip" data-domain="algorithms" onclick="setDomain('algorithms',this)">algorithms</button> | ||||||||||
|
|
@@ -131,9 +153,11 @@ <h3>04 Qiskit & PennyLane</h3> | |||||||||
| <button class="chip" data-domain="optimization" onclick="setDomain('optimization',this)">optimization</button> | ||||||||||
| <button class="chip" data-domain="simulation" onclick="setDomain('simulation',this)">simulation</button> | ||||||||||
| <button class="chip" data-domain="communication" onclick="setDomain('communication',this)">communication</button> | ||||||||||
| </span> | ||||||||||
| </div> | ||||||||||
|
|
||||||||||
| <div class="count" id="count">100 circuits</div> | ||||||||||
| <div id="tab-note" class="count" style="display:none"></div> | ||||||||||
| <div class="grid" id="grid"></div> | ||||||||||
|
|
||||||||||
| <div class="modal-overlay" id="overlay" onclick="if(event.target===this)closeModal()"> | ||||||||||
|
|
@@ -276,6 +300,26 @@ <h2 id="m-name">—</h2> | |||||||||
| }; | ||||||||||
|
|
||||||||||
| let _domain = ''; | ||||||||||
| let _tab = 'circuits'; | ||||||||||
|
|
||||||||||
| // Example shared artifacts (lres:/lqpu:). On a page served by a live node these | ||||||||||
| // are fetched from the relay; here they illustrate what a peer would discover. | ||||||||||
| const RESULTS = [ | ||||||||||
| {circuit:'bell_phi_plus', circuit_cid:'lcid:e86fd8b3…', shots:1024, backend:'aer_simulator', | ||||||||||
| counts:{'00':508,'11':516}, desc:'Bell state — perfectly correlated'}, | ||||||||||
| {circuit:'ghz_3', circuit_cid:'lcid:7a1c…', shots:2048, backend:'aer_simulator', | ||||||||||
| counts:{'000':1017,'111':1031}, desc:'3-qubit GHZ — all-0 or all-1'}, | ||||||||||
| {circuit:'grover_2q', circuit_cid:'lcid:9f4b…', shots:1024, backend:'ibm_kyiv', | ||||||||||
| counts:{'11':903,'00':47,'01':39,'10':35}, desc:'Grover marks |11⟩'}, | ||||||||||
| {circuit:'qrng_4', circuit_cid:'lcid:c2a0…', shots:1600, backend:'aer_simulator', | ||||||||||
| counts:{'0000':98,'0101':110,'1111':94,'1010':101,'0011':96,'1100':105,'0110':100,'1001':102}, desc:'4-bit quantum RNG — uniform'}, | ||||||||||
| ]; | ||||||||||
| const BACKENDS = [ | ||||||||||
| {name:'aer_simulator', provider:'local', qubits:32, sim:true, gates:['h','x','cx','rz','ry','rx','ccx'], desc:'Statevector simulator (bundled)'}, | ||||||||||
| {name:'ibm_kyiv', provider:'IBM', qubits:127, sim:false, gates:['ecr','id','rz','sx','x'], desc:'Eagle r3 superconducting QPU'}, | ||||||||||
| {name:'ionq_aria', provider:'IonQ', qubits:25, sim:false, gates:['gpi','gpi2','ms'], desc:'Trapped-ion, all-to-all connectivity'}, | ||||||||||
| {name:'quantinuum_h2', provider:'Quantinuum', qubits:56, sim:false, gates:['u1q','rz','zz'], desc:'Trapped-ion, high fidelity'}, | ||||||||||
| ]; | ||||||||||
|
|
||||||||||
| function setDomain(d, btn) { | ||||||||||
| _domain = d; | ||||||||||
|
|
@@ -284,7 +328,32 @@ <h2 id="m-name">—</h2> | |||||||||
| render(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function setTab(t, btn) { | ||||||||||
| _tab = t; | ||||||||||
| document.querySelectorAll('.tab').forEach(b => b.classList.remove('active')); | ||||||||||
| btn.classList.add('active'); | ||||||||||
| // domain chips + circuit search only make sense on the circuits tab | ||||||||||
| document.getElementById('domain-row').style.display = (t === 'circuits') ? 'contents' : 'none'; | ||||||||||
| const search = document.getElementById('search'); | ||||||||||
| search.placeholder = t === 'circuits' ? 'Search circuits…' | ||||||||||
| : t === 'results' ? 'Search results…' : 'Search backends…'; | ||||||||||
| const note = document.getElementById('tab-note'); | ||||||||||
| if (t === 'circuits') { note.style.display = 'none'; } | ||||||||||
| else { | ||||||||||
| note.style.display = ''; | ||||||||||
| note.textContent = 'Example shared artifacts — a node serving this page streams live ' | ||||||||||
| + t + ' from the relay.'; | ||||||||||
| } | ||||||||||
| render(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function render() { | ||||||||||
| if (_tab === 'results') return renderResults(); | ||||||||||
| if (_tab === 'backends') return renderBackends(); | ||||||||||
| return renderCircuits(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function renderCircuits() { | ||||||||||
| const q = document.getElementById('search').value.toLowerCase(); | ||||||||||
| const filtered = CIRCUITS.filter(c => { | ||||||||||
| if (_domain && c.domain !== _domain) return false; | ||||||||||
|
|
@@ -311,6 +380,55 @@ <h2 id="m-name">—</h2> | |||||||||
| }); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function renderResults() { | ||||||||||
| const q = document.getElementById('search').value.toLowerCase(); | ||||||||||
| const rows = RESULTS.filter(r => !q || r.circuit.includes(q) || r.backend.toLowerCase().includes(q)); | ||||||||||
|
Comment on lines
+383
to
+385
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): Search in The query is lower-cased, but only |
||||||||||
| document.getElementById('count').textContent = `${rows.length} result${rows.length===1?'':'s'}`; | ||||||||||
| const grid = document.getElementById('grid'); | ||||||||||
| grid.innerHTML = ''; | ||||||||||
| rows.forEach(r => { | ||||||||||
| const max = Math.max(...Object.values(r.counts)); | ||||||||||
| const bars = Object.entries(r.counts).sort((a,b)=>b[1]-a[1]).map(([bs,n]) => | ||||||||||
| `<div class="hist-row"><span class="bs">${_esc(bs)}</span> | ||||||||||
| <span class="hist-bar" style="width:${Math.round(120*n/max)}px"></span> | ||||||||||
| <span class="n">${n}</span></div>`).join(''); | ||||||||||
| const div = document.createElement('div'); | ||||||||||
| div.className = 'card'; | ||||||||||
| div.innerHTML = ` | ||||||||||
| <div class="card-name">${_esc(r.circuit)}</div> | ||||||||||
| <div class="card-domain" style="color:var(--green)">result · ${r.shots} shots · ${_esc(r.backend)}</div> | ||||||||||
| <div class="card-desc">${_esc(r.desc)}</div> | ||||||||||
| <div class="hist">${bars}</div> | ||||||||||
| <div class="card-meta"><span class="tag">${_esc(r.circuit_cid)}</span></div>`; | ||||||||||
| grid.appendChild(div); | ||||||||||
| }); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function renderBackends() { | ||||||||||
| const q = document.getElementById('search').value.toLowerCase(); | ||||||||||
| const rows = BACKENDS.filter(b => !q || b.name.toLowerCase().includes(q) | ||||||||||
| || b.provider.toLowerCase().includes(q) || b.gates.join(' ').includes(q)); | ||||||||||
|
Comment on lines
+409
to
+410
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Search in
Suggested change
|
||||||||||
| document.getElementById('count').textContent = `${rows.length} backend${rows.length===1?'':'s'}`; | ||||||||||
| const grid = document.getElementById('grid'); | ||||||||||
| grid.innerHTML = ''; | ||||||||||
| rows.forEach(b => { | ||||||||||
| const div = document.createElement('div'); | ||||||||||
| div.className = 'card'; | ||||||||||
| div.innerHTML = ` | ||||||||||
| <div class="card-name">${_esc(b.name)} | ||||||||||
| <span class="${b.sim?'pill-sim':'pill-hw'}">${b.sim?'simulator':'hardware'}</span></div> | ||||||||||
| <div class="card-domain" style="color:var(--accent2)">${_esc(b.provider)}</div> | ||||||||||
| <div class="card-desc">${_esc(b.desc)}</div> | ||||||||||
| <div class="kv"> | ||||||||||
| <span class="k">qubits</span><span class="v">${b.qubits}</span> | ||||||||||
| <span class="k">native</span><span class="v">${b.gates.map(_esc).join(', ')}</span> | ||||||||||
| </div> | ||||||||||
| <div class="card-meta"><span class="qubits">${b.qubits}q</span> | ||||||||||
| ${b.gates.slice(0,3).map(g=>`<span class="tag">${_esc(g)}</span>`).join('')}</div>`; | ||||||||||
| grid.appendChild(div); | ||||||||||
| }); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function _esc(s) { | ||||||||||
| return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); | ||||||||||
| } | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): Alpha suffixes on CSS color variables are invalid and will be ignored by the browser.
Values like
var(--accent)55andvar(--accent)0dare not valid CSS colors, soborder-colorandbackgroundwon’t apply. If you need transparency, usergba()or define separate variables (e.g.--accent-border,--accent-bg) with full color values such as#58a6ff55. The same issue applies to the pill styles (var(--green)18,var(--green)44,var(--orange)18,var(--orange)44).