microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billti/num2-sim

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/npm/qsharp/ux/estimatesOverview.tsx

317lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4// A component including the results table and the scatter chart together.
5// The results table is also a legend for the scatter chart.
6
7import { useState } from "preact/hooks";
8import { ColorMap } from "./colormap.js";
9import {
10 CreateSingleEstimateResult,
11 FrontierEntry,
12 ReData,
13 SingleEstimateResult,
14} from "./data.js";
15import { ResultsTable, Row } from "./resultsTable.js";
16import { Axis, PlotItem, ScatterChart, ScatterSeries } from "./scatterChart.js";
17
18const columnNames = [
19 "Run name",
20 "Estimate type",
21 "Qubit type",
22 "QEC scheme",
23 "Error budget",
24 "Logical qubits",
25 "Logical depth",
26 "Code distance",
27 "T states",
28 "T factories",
29 "T factory fraction",
30 "Runtime",
31 "rQOPS",
32 "Physical qubits",
33];
34
35const initialColumns = [0, 10, 13, 11, 12];
36
37const xAxis: Axis = {
38 isTime: true,
39 label: "Runtime",
40};
41
42const yAxis: Axis = {
43 isTime: false,
44 label: "Physical qubits",
45};
46
47function reDataToRow(input: ReData, color: string): Row {
48 const data = CreateSingleEstimateResult(input, 0);
49 const estimateType =
50 input.frontierEntries == null
51 ? "Single"
52 : "Frontier (" + input.frontierEntries.length + " items)";
53
54 return {
55 cells: [
56 data.jobParams.runName,
57 estimateType,
58 data.jobParams.qubitParams.name,
59 data.jobParams.qecScheme.name,
60 data.jobParams.errorBudget,
61 data.physicalCounts.breakdown.algorithmicLogicalQubits,
62 data.physicalCounts.breakdown.algorithmicLogicalDepth,
63 data.logicalQubit.codeDistance,
64 data.physicalCounts.breakdown.numTstates,
65 data.physicalCounts.breakdown.numTfactories,
66 data.physicalCountsFormatted.physicalQubitsForTfactoriesPercentage,
67 {
68 value: data.physicalCountsFormatted.runtime,
69 sortBy: data.physicalCounts.runtime,
70 },
71 data.physicalCounts.rqops,
72 data.physicalCounts.physicalQubits,
73 ],
74 color: color,
75 };
76}
77
78function frontierEntryToPlotEntry(entry: FrontierEntry): PlotItem {
79 return {
80 x: entry.physicalCounts.runtime,
81 y: entry.physicalCounts.physicalQubits,
82 label:
83 entry.physicalCountsFormatted.runtime +
84 ", physical qubits: " +
85 entry.physicalCountsFormatted.physicalQubits +
86 ", code distance: " +
87 entry.logicalQubit.codeDistance,
88 };
89}
90
91function reDataToRowScatter(data: ReData, color: string): ScatterSeries {
92 if (data.frontierEntries == null || data.frontierEntries.length === 0) {
93 return {
94 color: color,
95 items: [
96 {
97 x: data.physicalCounts.runtime,
98 y: data.physicalCounts.physicalQubits,
99 label:
100 data.physicalCountsFormatted.runtime +
101 ", physical qubits: " +
102 data.physicalCountsFormatted.physicalQubits +
103 ", code distance: " +
104 data.logicalQubit.codeDistance,
105 },
106 ],
107 };
108 }
109
110 return {
111 color: color,
112 items: data.frontierEntries.map(frontierEntryToPlotEntry),
113 };
114}
115
116function createRunNames(estimatesData: ReData[]): string[] {
117 // If there's only 1 entry, use the shared run name
118 if (estimatesData.length === 1) {
119 const name =
120 estimatesData[0].jobParams.sharedRunName ??
121 estimatesData[0].jobParams.qubitParams.name ??
122 estimatesData[0].jobParams.qecScheme.name ??
123 "estimate";
124
125 return [name];
126 }
127
128 const fields: string[][] = [];
129
130 estimatesData.forEach(() => {
131 fields.push([]);
132 });
133
134 // Could be multiple runs, e.g. against different algorithms.
135 addIfDifferent(fields, estimatesData, (data) => data.jobParams.sharedRunName);
136
137 addIfDifferent(
138 fields,
139 estimatesData,
140 (data) => data.jobParams.qubitParams.name,
141 );
142
143 addIfDifferent(
144 fields,
145 estimatesData,
146 (data) => data.jobParams.qecScheme.name,
147 );
148
149 addIfDifferent(fields, estimatesData, (data) =>
150 String(data.jobParams.errorBudget),
151 );
152
153 const proposedRunNames = fields.map((field) => field.join(", "));
154 if (new Set(proposedRunNames).size != proposedRunNames.length) {
155 // If there are duplicates, add the run index to the name.
156 return proposedRunNames.map(
157 (runName, index) => runName + " (" + index + ")",
158 );
159 }
160
161 return proposedRunNames;
162}
163
164function addIfDifferent(
165 fields: string[][],
166 estimatesData: ReData[],
167 fieldMethod: (data: ReData) => string,
168): void {
169 const arr = estimatesData.map(fieldMethod);
170
171 const set = new Set(arr);
172 if (set.size > 1) {
173 arr.forEach((field, index) => {
174 fields[index].push(field);
175 });
176 }
177}
178
179export function EstimatesOverview(props: {
180 estimatesData: ReData[];
181 colors: string[] | null;
182 runNames: string[] | null;
183 isSimplifiedView: boolean;
184 onRowDeleted: (rowId: string) => void;
185 setEstimate: (estimate: SingleEstimateResult | null) => void;
186 allowSaveImage: boolean;
187}) {
188 const [selectedRow, setSelectedRow] = useState<string | null>(null);
189 const [selectedPoint, setSelectedPoint] = useState<[number, number]>();
190
191 const runNameRenderingError =
192 props.runNames != null &&
193 props.runNames.length > 0 &&
194 props.runNames.length != props.estimatesData.length
195 ? "Warning: The number of runNames does not match the number of estimates. Ignoring provided runNames."
196 : "";
197
198 const runNames =
199 props.runNames != null &&
200 props.runNames.length == props.estimatesData.length
201 ? props.runNames
202 : createRunNames(props.estimatesData);
203
204 props.estimatesData.forEach((item, idx) => {
205 // Start indexing with 0 to match with the original object indexing.
206 item.jobParams.runName = runNames[idx];
207 });
208
209 function onPointSelected(seriesIndex: number, pointIndex: number): void {
210 if (seriesIndex < 0) {
211 // Point was deselected
212 onRowSelected("");
213 return;
214 }
215
216 const data = props.estimatesData[seriesIndex];
217 props.setEstimate(CreateSingleEstimateResult(data, pointIndex));
218 const rowId = props.estimatesData[seriesIndex].jobParams.runName;
219 setSelectedRow(rowId);
220 setSelectedPoint([seriesIndex, pointIndex]);
221 }
222
223 function onRowSelected(rowId: string) {
224 setSelectedRow(rowId);
225 if (!rowId) {
226 props.setEstimate(null);
227 setSelectedPoint(undefined);
228 } else {
229 const index = props.estimatesData.findIndex(
230 (data) => data.jobParams.runName === rowId,
231 );
232
233 if (index == -1) {
234 props.setEstimate(null);
235 setSelectedPoint(undefined);
236 } else {
237 const estimateFound = props.estimatesData[index];
238 setSelectedPoint([index, 0]);
239 props.setEstimate(CreateSingleEstimateResult(estimateFound, 0));
240 }
241 }
242 }
243
244 const colorRenderingError =
245 props.colors != null &&
246 props.colors.length > 0 &&
247 props.colors.length != props.estimatesData.length
248 ? "Warning: The number of colors does not match the number of estimates. Ignoring provided colors."
249 : "";
250
251 const colormap =
252 props.colors != null && props.colors.length == props.estimatesData.length
253 ? props.colors
254 : ColorMap(props.estimatesData.length);
255
256 function getResultTable() {
257 return (
258 <ResultsTable
259 columnNames={columnNames}
260 rows={props.estimatesData.map((dataItem, index) =>
261 reDataToRow(dataItem, colormap[index]),
262 )}
263 initialColumns={initialColumns}
264 selectedRow={selectedRow}
265 onRowSelected={onRowSelected}
266 onRowDeleted={props.onRowDeleted}
267 />
268 );
269 }
270
271 function getScatterChart() {
272 return (
273 <ScatterChart
274 xAxis={xAxis}
275 yAxis={yAxis}
276 data={props.estimatesData.map((dataItem, index) =>
277 reDataToRowScatter(dataItem, colormap[index]),
278 )}
279 onPointSelected={onPointSelected}
280 selectedPoint={selectedPoint}
281 allowSaveImage={props.allowSaveImage}
282 />
283 );
284 }
285
286 return (
287 <div className="qs-estimatesOverview">
288 {runNameRenderingError != "" && (
289 <div class="qs-estimatesOverview-error">{runNameRenderingError}</div>
290 )}
291 {colorRenderingError != "" && (
292 <div class="qs-estimatesOverview-error">{colorRenderingError}</div>
293 )}
294 {!props.isSimplifiedView ? (
295 <>
296 <details open>
297 <summary style="font-size: 1.5em; font-weight: bold; margin: 24px 8px;">
298 Results
299 </summary>
300 {getResultTable()}
301 </details>
302 <details open>
303 <summary style="font-size: 1.5em; font-weight: bold; margin: 24px 8px;">
304 Space-time diagram
305 </summary>
306 {getScatterChart()}
307 </details>
308 </>
309 ) : (
310 <>
311 {getResultTable()}
312 {getScatterChart()}
313 </>
314 )}
315 </div>
316 );
317}
318