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');