-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Stim circuit writer; update tutorial UI (#192)
Apply the suggestions from the review of PR #166. Add the option to generate the circuit as an ASCII art or a STIM code snippet. NOTE: I do not think that the name "tutorial" is appropriate for this alternative frontend. Currently, the folder does not contain any explanation of the code nor the commits provide a step-by-step guide (the commits have been squashed in the merge). --------- Co-authored-by: rryoung98 <ritsukiyoung98@gmail.com>
- Loading branch information
Showing
38 changed files
with
22,959 additions
and
22,270 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import logo from './tqec_logo.svg'; | ||
import './App.css'; | ||
import {Stage} from '@pixi/react' | ||
import TqecLibrary from './library' | ||
import TqecCode from './code' | ||
import formattedInfoTabContent from './formattedInfoTab'; | ||
|
||
/* The logo has been created as the SVG rendering of the ASCII: | ||
* | ||
* /---/ //---/ //---/ //---/ | ||
* // // / //-/ // | ||
* // //---/== //---/ //---/ | ||
* | ||
* with the web app: https://ivanceras.github.io/svgbob-editor/ | ||
*/ | ||
|
||
const handleTabClick = (tabId) => { | ||
// Hide all tab contents | ||
const tabContents = document.querySelectorAll('.Tab-content'); | ||
tabContents.forEach(content => { | ||
content.style.display = 'none'; | ||
}); | ||
|
||
// Show the clicked tab content | ||
const clickedTabContent = document.getElementById(`content${tabId}`); | ||
if (clickedTabContent) { | ||
clickedTabContent.style.display = 'block'; | ||
} | ||
|
||
// Optionally, you can perform additional actions when a tab is clicked | ||
console.log(`Tab ${tabId} clicked`); | ||
}; | ||
|
||
function App() { | ||
return ( | ||
<div className="App"> | ||
<header className="App-header"> | ||
<img src={logo} className="App-logo" alt="logo" /> | ||
</header> | ||
|
||
<div className="Tabs"> | ||
<button className="Tab-button" id="tab1" onClick={() => handleTabClick(1)}>Info</button> | ||
<button className="Tab-button" id="tab2" onClick={() => handleTabClick(2)}>Compose library</button> | ||
<button className="Tab-button" id="tab3" onClick={() => handleTabClick(3)}>Create code</button> | ||
{/* -- Add more tabs as needed -- */} | ||
</div> | ||
|
||
<div id="Tab-content"> | ||
<div id="content1" className="Tab-content active"> | ||
{/*-- Content for Tab 1 -- */} | ||
{formattedInfoTabContent} | ||
</div> | ||
|
||
<div id="content2" className="Tab-content"> | ||
{/*-- Content for Tab 2 -- */} | ||
|
||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center'}}> | ||
<p className="Comment-paragraph">Enter file name:</p> | ||
<input className="Cell-size" | ||
type="number" | ||
id="dxCell" | ||
placeholder="2" | ||
/> | ||
<p className="Comment-paragraph">x</p> | ||
<input className="Cell-size" | ||
type="number" | ||
id="dyCell" | ||
placeholder="2" | ||
/> | ||
</div> | ||
|
||
<Stage width={1400} height={900} options={{backgroundColor: "rgb(154, 193, 208)", antialias: true}}> | ||
<TqecLibrary /> | ||
</Stage> | ||
|
||
<p className="Comment-paragraph">Circuit-editing area:</p> | ||
<div style={{ display: 'flex', justifyContent: 'center' }}> | ||
<textarea | ||
className="Text-area" | ||
type="text" | ||
id="editableCircuitArea" | ||
placeholder="Edit the circuit here..." | ||
/> | ||
</div> | ||
|
||
</div> | ||
|
||
<div id="content3" className="Tab-content"> | ||
{/*-- Content for Tab 3 -- */} | ||
<Stage width={1400} height={1100} options={{backgroundColor: 'rgb(157, 191, 145)', antialias: true}}> | ||
<TqecCode /> | ||
</Stage> | ||
|
||
<p className="Comment-paragraph">Compact representation of the QEC code:</p> | ||
<div style={{ display: 'flex', justifyContent: 'center' }}> | ||
<textarea | ||
className="Text-area" | ||
type="text" | ||
id="codeSummary" | ||
placeholder="Code will appear here..." | ||
/> | ||
</div> | ||
|
||
</div> | ||
{/*-- Add more content areas as needed -- */} | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default App; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
import { useApp } from '@pixi/react' | ||
import { makeGrid } from '../library/grid' | ||
import { Container, Graphics } from 'pixi.js' | ||
import Position from '../library/position' | ||
import { button } from '../library/button' | ||
import Plaquette from '../library/plaquette' | ||
import PlaquetteType from './plaquette-type' | ||
import Circuit from '../library/circuit' | ||
import { savedPlaquettes, libraryColors } from '../library' | ||
import { Qubit } from '../library/qubit' | ||
import { GRID_SIZE_CODE_WORKSPACE, GUIDE_MAX_BOTTOM_RIGHT_CORNER_CODE_WORKSPACE, GUIDE_TOP_LEFT_CORNER_CODE_WORKSPACE } from '../constants' | ||
|
||
///////////////////////////////////////////////////////////// | ||
|
||
export default function TqecCode() { | ||
// Initialize the app | ||
let app = useApp(); | ||
|
||
// Remove all children from the stage to avoid rendering issues | ||
app.stage.removeChildren(); | ||
const gridSize = GRID_SIZE_CODE_WORKSPACE; | ||
const qubitRadius = 7; | ||
document.getElementById('dxCell').value = 2; | ||
document.getElementById('dyCell').value = 2; | ||
let plaquetteDx = parseInt(document.getElementById('dxCell').value); | ||
let plaquetteDy = parseInt(document.getElementById('dyCell').value); | ||
|
||
// Create the workspace | ||
const workspace = new Container(); | ||
workspace.name = 'workspace-code'; | ||
|
||
// Create the grid container | ||
const grid = makeGrid(app, gridSize); | ||
// We want the grid to be in the lowest layer | ||
workspace.addChildAt(grid, 0); | ||
|
||
///////////////////////////////////////////////////////////// | ||
|
||
// Add guide for the eyes for the plaquette boundaries. | ||
// They are located via the position of the top, left corner. | ||
// The first guide is where the plaquette is built, the other guides are for the library. | ||
const guideTopLeftCorner = GUIDE_TOP_LEFT_CORNER_CODE_WORKSPACE; | ||
let libraryTopLeftCorners = [[21, 3], [21, 7], [21, 11], [21, 15]]; | ||
const outline = new Graphics(); | ||
workspace.addChild(outline); | ||
|
||
///////////////////////////////////////////////////////////// | ||
|
||
// Add qubit positions to the workspace | ||
for (let x = 0; x <= app.screen.width/gridSize; x += 1) { | ||
for (let y = 0; y <= app.screen.height/gridSize; y += 1) { | ||
// Skip every other qubit | ||
if ( (x+y) % 2 === 1 ) | ||
continue; | ||
// Create a qubit | ||
const pos = new Position(x*gridSize, y*gridSize, qubitRadius-2); | ||
workspace.addChild(pos); | ||
} | ||
} | ||
const num_background_children = workspace.children.length; | ||
|
||
///////////////////////////////////////////////////////////// | ||
|
||
const infoButton = button('Library of plaquettes', libraryTopLeftCorners[0][0]*gridSize, 1*gridSize, 'orange', 'black'); | ||
workspace.addChild(infoButton); | ||
|
||
// Select the qubits that are part of a plaquette | ||
const importPlaquettesButton = button('Import plaquettes from composer', gridSize, 1*gridSize, 'white', 'black'); | ||
workspace.addChild(importPlaquettesButton); | ||
let codePlaquettes = []; | ||
|
||
importPlaquettesButton.on('click', (_e) => { | ||
outline.clear() | ||
plaquetteDx = parseInt(document.getElementById('dxCell').value); | ||
plaquetteDy = parseInt(document.getElementById('dyCell').value); | ||
libraryTopLeftCorners = [[21, 3], [21, 3+plaquetteDy+2], [21, 3+(plaquetteDy+2)*2], [21, 3+(plaquetteDy*2)*3]] | ||
outline.lineStyle(2, 'lightcoral'); | ||
// Add workspace guidelines. | ||
let y0 = guideTopLeftCorner[1]; | ||
let message = ''; | ||
while (y0 + plaquetteDy <= GUIDE_MAX_BOTTOM_RIGHT_CORNER_CODE_WORKSPACE[0]) { | ||
let x0 = guideTopLeftCorner[0]; | ||
while (x0 + plaquetteDx <= GUIDE_MAX_BOTTOM_RIGHT_CORNER_CODE_WORKSPACE[1]) { | ||
const x1 = x0 + plaquetteDx; | ||
const y1 = y0 + plaquetteDy; | ||
outline.moveTo(x0*gridSize, y0*gridSize); | ||
outline.lineTo(x1*gridSize, y0*gridSize); | ||
outline.lineTo(x1*gridSize, y1*gridSize); | ||
outline.lineTo(x0*gridSize, y1*gridSize); | ||
outline.lineTo(x0*gridSize, y0*gridSize); | ||
x0 += plaquetteDx; | ||
message += ' .'; | ||
} | ||
y0 += plaquetteDy; | ||
message += '\n'; | ||
} | ||
// Add library guidelines. | ||
for (const [x0, y0] of libraryTopLeftCorners) { | ||
const x1 = x0 + plaquetteDx; | ||
const y1 = y0 + plaquetteDy; | ||
outline.moveTo(x0*gridSize, y0*gridSize); | ||
outline.lineTo(x1*gridSize, y0*gridSize); | ||
outline.lineTo(x1*gridSize, y1*gridSize); | ||
outline.lineTo(x0*gridSize, y1*gridSize); | ||
outline.lineTo(x0*gridSize, y0*gridSize); | ||
} | ||
// Create the compact representation of the (empty) QEC code | ||
const codesummary = document.getElementById('codeSummary'); | ||
codesummary.value = message; | ||
// Add library plaquettes. | ||
//const library_workspace = document.getElementsByName('workspace-library'); | ||
let plaquetteTypes = []; | ||
//const numPlaquettes = savedPlaquettes.length; | ||
savedPlaquettes.forEach((plaq, index) => { | ||
if (plaq.name !== 'WIP plaquette') { | ||
let qubits = []; | ||
plaq.qubits.forEach((q) => { | ||
const qubit = new Qubit(q.globalX, q.globalY, q.Radius); | ||
qubit.name = q.name; | ||
qubit.updateLabel(); | ||
workspace.addChild(qubit); | ||
qubits.push(qubit); | ||
}); | ||
// Recall that plaquette names are like "plaquette 12", starting from "plaquette 1" | ||
const plaquette_id = parseInt(plaq.name.match(/\d+/)[0]); | ||
const base_translate_vector = {x: guideTopLeftCorner[0] - libraryTopLeftCorners[plaquette_id-1][0], | ||
y: guideTopLeftCorner[1] - libraryTopLeftCorners[plaquette_id-1][1]}; | ||
const p_type = new PlaquetteType(qubits, libraryColors[index], num_background_children, base_translate_vector) | ||
p_type.name = plaq.name; | ||
plaquetteTypes.push(p_type); | ||
workspace.addChildAt(p_type, num_background_children); | ||
} | ||
}); | ||
// FIXME: Some plaquettes in the red-delimited space may already be there. | ||
// Compose the compact representation of the code accordingly. | ||
}); | ||
|
||
///////////////////////////////////////////////////////////// | ||
|
||
// Undo button, meaning that the last plaquette added is removed. | ||
const undoButton = button('Remove last plaquette', gridSize, 2*gridSize, 'white', 'black'); | ||
workspace.addChild(undoButton); | ||
|
||
undoButton.on('click', (_e) => { | ||
if (workspace.children[num_background_children] instanceof Plaquette | ||
&& !(workspace.children[num_background_children] instanceof PlaquetteType) ) { | ||
workspace.removeChildAt(num_background_children); | ||
// FIXME: correct the compact representation of the code! | ||
} | ||
}); | ||
|
||
///////////////////////////////////////////////////////////// | ||
|
||
// Create a button to de-select all qubits | ||
const downloadCodeButton = button('Download QEC code', gridSize, 21*gridSize, 'white', 'black'); | ||
workspace.addChild(downloadCodeButton); | ||
|
||
downloadCodeButton.on('click', (_e) => { | ||
if (codePlaquettes.length === 0) return; | ||
|
||
let message = ''; | ||
// Add info on cell size | ||
message += 'This is the complete QEC code.\n' | ||
let counter = 0; | ||
codePlaquettes.forEach((plaq) => { | ||
if (plaq.name !== 'WIP plaquette') { | ||
message += '###############\n' | ||
message += `# plaquette ${counter} #\n` | ||
message += '###############\n\n' | ||
plaq.children.forEach((child) => { | ||
if (child instanceof Circuit) { | ||
console.log('circuit to add'); | ||
message += child.art.text; | ||
message += '\n\n\n'; | ||
console.log(message); | ||
} | ||
}); | ||
counter += 1; | ||
} | ||
}); | ||
const blob = new Blob([message], { type: 'text/plain' }); | ||
const url = URL.createObjectURL(blob); | ||
|
||
const link = document.createElement('a'); | ||
link.href = url; | ||
link.download = 'qec_code.txt'; | ||
document.body.appendChild(link); | ||
link.click(); | ||
document.body.removeChild(link); | ||
URL.revokeObjectURL(url); | ||
}); | ||
|
||
///////////////////////////////////////////////////////////// | ||
|
||
// Add workspace to the stage | ||
workspace.visible = true; | ||
app.stage.addChild(workspace); | ||
|
||
return null; | ||
} |
Oops, something went wrong.