diff --git a/html/export.partial.html b/html/export.partial.html index af7d65a9..1054cbde 100644 --- a/html/export.partial.html +++ b/html/export.partial.html @@ -20,14 +20,23 @@

         
 
+        
+ OpenQASM +
+    +
+

+        
+ +
Simulation Data JSON - Output amplitudes, detector results, display data, etc.
-   - +   +
-

+            

         
diff --git a/package.json b/package.json index 07580244..01ca317a 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "scripts": { "build": "grunt build-src", + "build-debug": "grunt build-debug && cp ./out/quirk.html /mnt/x -f", "test": "grunt test", "test-chrome": "grunt test-chrome", "test-firefox": "grunt test-firefox", diff --git a/src/ui/exports.js b/src/ui/exports.js index 14b7bd78..92a24e29 100644 --- a/src/ui/exports.js +++ b/src/ui/exports.js @@ -77,6 +77,141 @@ function initExports(revision, mostRecentStats, obsIsAnyOverlayShowing) { }, 1000); }); + const convertJsonToQasm = (jsonText) => { + // X, Y, Z, H, S, T, Sdg, Tdg, Swap, CX, CCX, RX, RY, RZ, SX, SXdg, Measure, CRX, CRY, CRZ + const map = { + X: "x", + Y: "y", + Z: "z", + H: "h", + "Z^½": "s", + "Z^¼": "t", + "Z^-½": "sdg", + "Z^-¼": "tdg", + "Swap": "swap", + "•": "c", + "Rxft": "rx", + "Ryft": "ry", + "Rzft": "rz", + "X^½": "sx", + "X^-½": "sxdg", + "Measure": "measure" + } + let qasmString = 'OPENQASM 2.0;include "qelib1.inc";';//here + //noinspection UnusedCatchParameterJS + var json = "" + + const handleControlGates = (arr) => { + var qasmStr = ""; + const controlGate = "•"; + const acceptedGates = { + 1: ['H', 'X', 'Y', 'Z', 'Rxft', 'Ryft', 'Rzft', 'X^½', 'Swap'], + 2: ['X'] + } + // the way quirk works, if you have a control in any column, all gates in the column are controlled + var numCtrls = arr.filter(elem => elem == controlGate).length; + if (numCtrls > 2) throw new Error("Too many controls (max 2)"); + + //check for unsupported gates + // if (arr.filter(elem => !acceptedGates[numCtrls].includes(elem) && elem != 1 && elem != controlGate).length !== 0) { + // // check for invalid control ops + // //TODO - better logging? + // throw new Error("Invalid circuit - some controlled operations are not supported!"); + // } + + //check for at least 1 supported gate + if (arr.filter(elem => elem !== controlGate && elem !== 1).length === 0) { + throw new Error("Invalid circuit - controlled operation not specified!") + } + + + const ctrlString = "c".repeat(numCtrls); + const ctrlQubits = ` q[${arr.indexOf("•")}],` + (numCtrls == 2 ? `q[${arr.lastIndexOf("•")}],` : ``); + + arr.forEach((gate, idx) => { + if (gate == controlGate || gate == 1) return; + //if (!acceptedGates[numCtrls].includes(gate)) throw new Error(`Unsupported control gate (${ctrlString+map[gate]})`) + if (typeof gate == "string") { + if (!acceptedGates[numCtrls].includes(gate)) throw new Error(`Unsupported control gate (${ctrlString+gate})`) + var targetQubits; + if (gate == "Swap") { + if (arr.filter(elem => elem == 'Swap').length != 2) throw new Error('Wrong number of swaps!'); + targetQubits = `q[${arr.indexOf('Swap')}],q[${arr.lastIndexOf('Swap')}];`;//here + arr[arr.lastIndexOf('Swap')] = arr[arr.indexOf('Swap')] = 1; + } + else targetQubits = `q[${idx}];`;//here + qasmStr = qasmStr + ctrlString + map[gate] + ctrlQubits + targetQubits; + //console.log(qasmStr); + } + else { //parametrized gate + if(!acceptedGates[numCtrls].includes(gate["id"])) throw new Error(`Unsupported control gate (${ctrlString+gate["id"]})`) + qasmStr = qasmStr + ctrlString + `${map[gate["id"]]}(${gate["arg"]})${ctrlQubits}q[${idx}];`;//here + //console.log(qasmStr); + } + + }); + return qasmStr; + + } + + + try { + json = JSON.parse(jsonText) + const cols = json["cols"]; + if (cols.length === 0) return "Empty circuit"; + const numQubits = Math.max(...(cols.map((arr) => arr.length))); + const numCbits = cols.filter(arr => arr.includes("Measure")).length; + + qasmString += `qreg q[${numQubits}];`;//here + if (numCbits > 0) qasmString +=`creg c[${numCbits}];`;//here + + var measurements = 0; + + cols.forEach((col) => { + //var measureStr = ""; + if (col.includes('Rxft') || col.includes('Ryft') || col.includes('Rzft')) + throw new Error("R*ft gates not supported, please provide a time-independent parameter") + + // if col contains controls, parse it fully and move on + if (col.includes("•")) { + qasmString += handleControlGates(col); + return; + } + + + // no controls left! + col.forEach((gate, idx) => { + if (gate == 1) return; + if (typeof gate == "string") { + if (!Object.keys(map).includes(gate)) throw new Error("Unsupported gate!"); + if (gate == "Measure") { + qasmString += `measure q[${idx}]->c[${measurements}];`;//here + measurements = measurements + 1; + return; + } + if (gate == "Swap") { + if (col.filter(elem => elem == 'Swap').length != 2) throw new Error('Wrong number of swaps!'); + var targetQubits = ` q[${col.indexOf('Swap')}],q[${col.lastIndexOf('Swap')}];`;//here + col[col.lastIndexOf('Swap')] = col[col.indexOf('Swap')] = 1; + qasmString += map[gate] + targetQubits; + return; + } + qasmString += map[gate] + ` q[${idx}];`;//here + } + else if(typeof gate == "object") { + if (!Object.keys(map).includes(gate["id"])) throw new Error("Unsupported gate!"); + qasmString += `${map[gate["id"]]}(${gate["arg"]}) q[${idx}];`;//here + } + }); + }); + } + catch(e) { + console.error(e) + return "Invalid Circuit JSON." + } + return qasmString; + } + // Export escaped link. (() => { const linkElement = /** @type {HTMLAnchorElement} */ document.getElementById('export-escaped-anchor'); @@ -107,6 +242,25 @@ function initExports(revision, mostRecentStats, obsIsAnyOverlayShowing) { }); })(); + // Export QASM + (() => { + const qasmTextElement = /** @type {HTMLPreElement} */ document.getElementById('export-qasm-pre'); + const copyButton = /** @type {HTMLButtonElement} */ document.getElementById('export-qasm-copy-button'); + const copyResultElement = /** @type {HTMLElement} */ document.getElementById('export-qasm-copy-result'); + setupButtonElementCopyToClipboard(copyButton, qasmTextElement, copyResultElement); + revision.latestActiveCommit().subscribe(jsonText => { + //noinspection UnusedCatchParameterJS + //debugger; + try { + let val = convertJsonToQasm(jsonText); + qasmTextElement.innerText = val; + } catch (_) { + console.error("ERROR") + qasmTextElement.innerText = jsonText; + } + }); + })(); + // Export final output. (() => { const outputTextElement = /** @type {HTMLPreElement} */ document.getElementById('export-amplitudes-pre');