Skip to content

Commit

Permalink
Add option to export simulation state as JSON
Browse files Browse the repository at this point in the history
- Define Gate.processedStatsToJsonFunc
- Tranpose density matrix data for density matrix display
- Add processedStatsToJsonFunc to most displays
- Track most recent CircuitStats in main
- Added "to readable json" methods to Matrix and CircuitStats
  • Loading branch information
Strilanc committed Mar 23, 2019
1 parent 34e02ff commit f09cd08
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 13 deletions.
10 changes: 9 additions & 1 deletion html/export.partial.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@
<button tabindex="102" id="download-offline-copy-button" style="width:600px; height:60px;">Download ...</button>
</div>

<strong>JSON</strong> - Parsable representation of current circuit.
<strong>Circuit JSON</strong> - Parsable representation of current circuit.
<div style="margin: 10px 0 0 20px;">
<button tabindex="103" id="export-json-copy-button">Copy to Clipboard</button>&nbsp;&nbsp;<span id="export-json-copy-result"></span>
<br>
<pre tabindex="104" id="export-circuit-json-pre" style="overflow:auto; max-width:600px; max-height:60px; border: 1px solid black; padding:5px; margin:2px;"></pre>
</div>

<br>
<strong>Simulation Data JSON</strong> - Output amplitudes, detector results, display data, etc.
<div style="margin: 10px 0 0 20px;">
<button tabindex="103" id="export-amplitudes-button">Generate and Copy to Clipboard</button>&nbsp;<span id="export-amplitudes-result"></span>
<br>
<pre tabindex="105" id="export-amplitudes-pre" style="overflow:auto; max-width:600px; max-height:60px; border: 1px solid black; padding:5px; margin:2px;"></pre>
</div>
</div>
</div>
57 changes: 56 additions & 1 deletion src/circuit/CircuitStats.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {CircuitShaders} from "src/circuit/CircuitShaders.js"
import {KetTextureUtil} from "src/circuit/KetTextureUtil.js"
import {Controls} from "src/circuit/Controls.js"
import {DetailedError} from "src/base/DetailedError.js"
import {Matrix} from "src/math/Matrix.js"
import {Matrix, complexVectorToReadableJson} from "src/math/Matrix.js"
import {Shaders} from "src/webgl/Shaders.js"
import {Serializer} from "src/circuit/Serializer.js"
import {Util} from "src/base/Util.js"
Expand Down Expand Up @@ -108,6 +108,48 @@ class CircuitStats {
return this._qubitDensities[col][wireIndex];
}

/**
* Converts the circuit stats into an exportable JSON object.
* @returns {!object}
*/
toReadableJson() {
return {
output_amplitudes: complexVectorToReadableJson(this.finalState.getColumn(0)),
time_parameter: this.time,
circuit: Serializer.toJson(this.circuitDefinition),
chance_of_surviving_to_each_column: this._survivalRates,
computed_bloch_vectors_by_column_then_wire: this._qubitDensities.map(
col => col.map(singleQubitDensityMatrixToReadableJson)
),
displays: this._customStatsToReadableJson()
};
}

_customStatsToReadableJson() {
let result = [];
for (let [key, data] of this._customStatsProcessed.entries()) {
let [col, row] = key.split(':');
row = parseInt(row);
col = parseInt(col);
let gate = this.circuitDefinition.columns[col].gates[row];
if (gate.processedStatsToJsonFunc !== undefined) {
data = gate.processedStatsToJsonFunc(data);
}
result.push({
location: {
wire: row,
column: col,
},
type: {
serialized_id: gate.serializedId,
name: gate.name,
},
data
});
}
return result;
}

/**
* Determines how often the circuit evaluation survives to the given column, without being post-selected out.
*
Expand Down Expand Up @@ -341,6 +383,19 @@ class CircuitStats {
}
}

/**
* @param {!Matrix} matrix
*/
function singleQubitDensityMatrixToReadableJson(matrix) {
if (matrix.hasNaN()) {
return null;
}
let [x, y, z] = matrix.qubitDensityMatrixToBlochVector();
x *= -1;
z *= -1;
return {x, y, z};
}

CircuitStats.EMPTY = CircuitStats.withNanDataFromCircuitAtTime(CircuitDefinition.EMPTY, 0);

export {CircuitStats}
16 changes: 16 additions & 0 deletions src/circuit/Gate.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ class Gate {
* @type {undefined|!function(!Float32Array, !CircuitDefinition, !int, !int) : *}
*/
this.customStatPostProcesser = undefined;
/**
* Returns a json form of the custom-stat data, used when exporting it.
* @type {undefined|!function(data: *) : *}
*/
this.processedStatsToJsonFunc = undefined;
/** @type {!Array.<!Gate>} A list of size variants of this gate.*/
this.gateFamily = [this];

Expand Down Expand Up @@ -262,6 +267,7 @@ class Gate {
g.customAfterOperation = this.customAfterOperation;
g.customStatTexturesMaker = this.customStatTexturesMaker;
g.customStatPostProcesser = this.customStatPostProcesser;
g.processedStatsToJsonFunc = this.processedStatsToJsonFunc;
g.width = this.width;
g.height = this.height;
g.isSingleQubitDisplay = this.isSingleQubitDisplay;
Expand Down Expand Up @@ -926,6 +932,16 @@ class GateBuilder {
return this;
}

/**
* Specifies how to convert custom stats data into json when exporting.
* @param {undefined|!function(data: *) : *} jsonFunc
* @returns {!GateBuilder}
*/
setProcessedStatsToJsonFunc(jsonFunc) {
this.gate.processedStatsToJsonFunc = jsonFunc;
return this;
}

/**
* @param {*} tag
* @returns {!GateBuilder}
Expand Down
6 changes: 3 additions & 3 deletions src/draw/MathPainter.js
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ class MathPainter {
let traceCouplingsWith = cellTraceFunc => painter.trace(trace => {
for (let row = 0; row < numRows; row++) {
for (let col = 0; col < numCols; col++) {
let k = (row + col * numRows) * 2;
let k = (row * numCols + col) * 2;
cellTraceFunc(
trace,
buf[k],
Expand Down Expand Up @@ -625,8 +625,8 @@ class MathPainter {
`Probability of |${Util.bin(c, n)}⟩` :
`Coupling of |${Util.bin(r, n)}⟩ to ⟨${Util.bin(c, n)}|`,
(c, r, v) => c === r ?
(matrix.cell(r, c).real*100).toFixed(4) + "%" :
matrix.cell(r, c).toString(new Format(false, 0, 6, ", ")));
(matrix.cell(c, r).real*100).toFixed(4) + "%" :
matrix.cell(c, r).toString(new Format(false, 0, 6, ", ")));
}
}

Expand Down
28 changes: 27 additions & 1 deletion src/gates/AmplitudeDisplay.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {Complex} from "src/math/Complex.js"
import {Config} from "src/Config.js"
import {CircuitShaders} from "src/circuit/CircuitShaders.js"
import {Gate} from "src/circuit/Gate.js"
import {GatePainting} from "src/draw/GatePainting.js"
import {GateShaders} from "src/circuit/GateShaders.js"
import {Format} from "src/base/Format.js"
import {MathPainter} from "src/draw/MathPainter.js"
import {Matrix} from "src/math/Matrix.js"
import {Matrix, complexVectorToReadableJson, realVectorToReadableJson} from "src/math/Matrix.js"
import {Point} from "src/math/Point.js"
import {Util} from "src/base/Util.js"
import {WglArg} from "src/webgl/WglArg.js"
Expand Down Expand Up @@ -417,6 +418,30 @@ function paintErrorIfPresent(args, isIncoherent) {
}
}

/**
* @param customStats
*/
function customStatsToJsonData(customStats) {
let {probabilities, superposition, phaseLockIndex} = customStats;
let result = {
coherence_measure: superposition !== undefined ? 1 : 0,
superposition_phase_locked_state_index: phaseLockIndex === undefined ? null : phaseLockIndex,
probabilities: null,
amplitudes: null,

};
if (probabilities !== undefined) {
let n = probabilities._width * probabilities._height;
result['probabilities'] = realVectorToReadableJson(
new Matrix(1, n, probabilities._buffer).getColumn(0).map(e => Math.pow(Complex.realPartOf(e), 2)));
}
if (superposition !== undefined) {
let n = superposition._width * superposition._height;
result['superposition'] = complexVectorToReadableJson(new Matrix(1, n, superposition._buffer).getColumn(0));
}
return result;
}

let AmplitudeDisplayFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
setSerializedId("Amps" + span).
setSymbol("Amps").
Expand All @@ -428,6 +453,7 @@ let AmplitudeDisplayFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
setStatTexturesMaker(ctx =>
amplitudeDisplayStatTextures(ctx.stateTrader.currentTexture, ctx.controls, ctx.row, span)).
setStatPixelDataPostProcessor((val, def) => processOutputs(span, val, def)).
setProcessedStatsToJsonFunc(customStatsToJsonData).
setDrawer(AMPLITUDE_DRAWER_FROM_CUSTOM_STATS));

export {
Expand Down
5 changes: 4 additions & 1 deletion src/gates/DensityMatrixDisplay.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function densityPixelsToMatrix(pixels, circuitDefinition, col, row) {
}

let isMeasuredMask = circuitDefinition.colIsMeasuredMask(col) >> row;
return decohereMeasuredBitsInDensityMatrix(new Matrix(d, d, pixels), isMeasuredMask);
return decohereMeasuredBitsInDensityMatrix(new Matrix(d, d, pixels), isMeasuredMask).transpose();
}

/**
Expand Down Expand Up @@ -186,6 +186,9 @@ function largeDensityMatrixDisplayMaker(span, builder) {
setSerializedId("Density" + span).
setWidth(span).
setDrawer(DENSITY_MATRIX_DRAWER_FROM_CUSTOM_STATS).
setProcessedStatsToJsonFunc(data => {
return {density_matrix: data.toReadableJson()};
}).
setStatTexturesMaker(ctx => densityDisplayStatTexture(
ctx.stateTrader.currentTexture, ctx.wireCount, ctx.controls, ctx.row, span)).
setStatPixelDataPostProcessor(densityPixelsToMatrix);
Expand Down
20 changes: 19 additions & 1 deletion src/gates/ProbabilityDisplay.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {Complex} from "src/math/Complex.js"
import {Config} from "src/Config.js"
import {Gate} from "src/circuit/Gate.js"
import {GatePainting} from "src/draw/GatePainting.js"
Expand All @@ -20,6 +21,7 @@ import {MathPainter} from "src/draw/MathPainter.js"
import {Matrix} from "src/math/Matrix.js"
import {Point} from "src/math/Point.js"
import {Rect} from "src/math/Rect.js"
import {Seq} from "src/base/Seq.js"
import {Shaders} from "src/webgl/Shaders.js"
import {Util} from "src/base/Util.js"
import {WglConfiguredShader} from "src/webgl/WglConfiguredShader.js"
Expand Down Expand Up @@ -98,6 +100,20 @@ function probabilityPixelsToColumnVector(pixels, span) {
return new Matrix(1, n, buf);
}

/**
* @param {!Matrix} data
* @returns {!{probabilities: !float[]}}
*/
function probabilityDataToJson(data) {
return {
probabilities: Seq.range(data.height()).map(k => Complex.realPartOf(data.cell(0, k))).toArray()
};
}

/**
* @param {!GateDrawParams} args
* @private
*/
function _paintMultiProbabilityDisplay_grid(args) {
let {painter, rect: {x, y, w, h}} = args;
let n = 1 << args.gate.height;
Expand Down Expand Up @@ -258,6 +274,7 @@ function multiChanceGateMaker(span, builder) {
setStatTexturesMaker(ctx =>
probabilityStatTexture(ctx.stateTrader.currentTexture, ctx.controlsTexture, ctx.row, span)).
setStatPixelDataPostProcessor(pixels => probabilityPixelsToColumnVector(pixels, span)).
setProcessedStatsToJsonFunc(probabilityDataToJson).
setDrawer(GatePainting.makeDisplayDrawer(paintMultiProbabilityDisplay));
}

Expand Down Expand Up @@ -288,5 +305,6 @@ export {
ProbabilityDisplayFamily,
probabilityStatTexture,
probabilityPixelsToColumnVector,
amplitudesToProbabilities
amplitudesToProbabilities,
probabilityDataToJson,
};
7 changes: 6 additions & 1 deletion src/gates/SampleDisplay.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ import {MathPainter} from "src/draw/MathPainter.js"
import {Point} from "src/math/Point.js"
import {Rect} from "src/math/Rect.js"
import {Util} from "src/base/Util.js"
import {probabilityStatTexture, probabilityPixelsToColumnVector} from "src/gates/ProbabilityDisplay.js"
import {
probabilityStatTexture,
probabilityPixelsToColumnVector,
probabilityDataToJson
} from "src/gates/ProbabilityDisplay.js"

/**
* @param {!GateDrawParams} args
Expand Down Expand Up @@ -106,6 +110,7 @@ let SampleDisplayFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
probabilityStatTexture(ctx.stateTrader.currentTexture, ctx.controlsTexture, ctx.row, span)).
setStatPixelDataPostProcessor(e => probabilityPixelsToColumnVector(e, span)).
promiseHasNoNetEffectOnStateVectorButStillRequiresDynamicRedraw().
setProcessedStatsToJsonFunc(probabilityDataToJson).
setDrawer(GatePainting.makeDisplayDrawer(paintSampleDisplay)).
setExtraDisableReasonFinder(args => args.isNested ? "can't\nnest\ndisplays\n(sorry)" : undefined));

Expand Down
5 changes: 4 additions & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ hookErrorHandler();
import {doDetectIssues} from "src/issues.js"
doDetectIssues();

import {CircuitStats} from "src/circuit/CircuitStats.js"
import {CooldownThrottle} from "src/base/CooldownThrottle.js"
import {Config} from "src/Config.js"
import {DisplayedInspector} from "src/ui/DisplayedInspector.js"
Expand Down Expand Up @@ -79,6 +80,7 @@ const inspectorDiv = document.getElementById("inspectorDiv");
/** @type {ObservableValue.<!DisplayedInspector>} */
const displayed = new ObservableValue(
DisplayedInspector.empty(new Rect(0, 0, canvas.clientWidth, canvas.clientHeight)));
const mostRecentStats = new ObservableValue(CircuitStats.EMPTY);
/** @type {!Revision} */
let revision = Revision.startingAt(displayed.get().snapshot());

Expand Down Expand Up @@ -132,6 +134,7 @@ const redrawNow = () => {

let shown = syncArea(displayed.get()).previewDrop();
let stats = simulate(shown.displayedCircuit.circuitDefinition);
mostRecentStats.set(stats);

let size = desiredCanvasSizeFor(shown);
canvas.width = size.w;
Expand Down Expand Up @@ -275,7 +278,7 @@ canvasDiv.addEventListener('mouseleave', () => {

let obsIsAnyOverlayShowing = new ObservableSource();
initUrlCircuitSync(revision);
initExports(revision, obsIsAnyOverlayShowing.observable());
initExports(revision, mostRecentStats, obsIsAnyOverlayShowing.observable());
initForge(revision, obsIsAnyOverlayShowing.observable());
initUndoRedo(revision, obsIsAnyOverlayShowing.observable());
initClear(revision, obsIsAnyOverlayShowing.observable());
Expand Down
27 changes: 26 additions & 1 deletion src/math/Matrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ class Matrix {
return this._buffer;
}

/**
* Encodes the matrix into JSON that could easily be read by humans or processed by external programs.
* @returns {*}
*/
toReadableJson() {
return seq(this.rows()).map(complexVectorToReadableJson).toArray();
}
/**
* @returns {!Array.<!Array.<Complex>>}
*/
Expand Down Expand Up @@ -1426,4 +1433,22 @@ Matrix.PAULI_Z = Matrix.square(1, 0, 0, -1);
*/
Matrix.HADAMARD = Matrix.square(1, 1, 1, -1).times(Math.sqrt(0.5));

export {Matrix}
/**
* Encodes a complex vector into JSON that could easily be read by humans or processed by external programs.
* @param {!Iterable.<!Complex>} vector
* @returns {*}
*/
function complexVectorToReadableJson(vector) {
return seq(vector).map(e => {return {real: Complex.realPartOf(e), imag: Complex.imagPartOf(e)}; }).toArray();
}

/**
* Encodes a real vector into JSON that could easily be read by humans or processed by external programs.
* @param {!Iterable.<!Complex>} vector
* @returns {*}
*/
function realVectorToReadableJson(vector) {
return seq(vector).map(Complex.realPartOf).toArray();
}

export {Matrix, complexVectorToReadableJson, realVectorToReadableJson}
Loading

0 comments on commit f09cd08

Please sign in to comment.