qsharp-lang 1.0.23-dev → 1.0.25-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,385 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ import { useRef, useState } from "preact/hooks";
5
+
6
+ export type CellValue = string | number | { value: string; sortBy: number };
7
+
8
+ // Note: column 0 is expected to be unique amongst all rows
9
+ export function ResultsTable(props: {
10
+ columnNames: string[];
11
+ rows: CellValue[][];
12
+ initialColumns: number[];
13
+ ensureSelected: boolean;
14
+ onRowSelected(rowId: string): void;
15
+ onRowDeleted(rowId: string): void;
16
+ }) {
17
+ const [showColumns, setShowColumns] = useState(props.initialColumns);
18
+ const [sortColumn, setSortColumn] = useState<{
19
+ columnId: number;
20
+ ascending: boolean;
21
+ } | null>(null);
22
+ const [selectedRow, setSelectedRow] = useState<string>("");
23
+ const [showColumnMenu, setShowColumnMenu] = useState(false);
24
+ const [showRowMenu, setShowRowMenu] = useState("");
25
+
26
+ if (!selectedRow && props.ensureSelected && props.rows.length > 0) {
27
+ const rowId = props.rows[0][0].toString();
28
+ setSelectedRow(rowId);
29
+ props.onRowSelected(rowId);
30
+ }
31
+
32
+ // Use to track the column being dragged
33
+ const draggingCol = useRef("");
34
+
35
+ /*
36
+ Note: Drag and drop events can occur faster than preact reconciles state.
37
+ This causes challenges where one event will set state, and the next event
38
+ that needs to use the latest state will still see old state.
39
+
40
+ So don't apply state changes in the drag and drop handlers. Instead, set
41
+ styles directly, and just update state if the 'drop' event changes column
42
+ order.
43
+ */
44
+
45
+ function onDragStart(ev: DragEvent) {
46
+ if (!(ev.target instanceof HTMLElement)) return;
47
+ const colid = ev.target.closest("th")?.dataset["colid"];
48
+ draggingCol.current = colid!;
49
+ ev.dataTransfer!.dropEffect = "move";
50
+ }
51
+
52
+ function onDragEnter(ev: DragEvent) {
53
+ // Get the column id of the column being entered
54
+ if (!(ev.target instanceof HTMLElement)) return;
55
+ const thisColId = ev.target.closest("th")?.dataset["colid"];
56
+ if (!thisColId || !draggingCol.current) return;
57
+
58
+ // If this column is different to the column being dragged, add the CSS class
59
+ if (draggingCol.current !== thisColId) {
60
+ ev.preventDefault();
61
+ ev.dataTransfer!.dropEffect = "move";
62
+ ev.target
63
+ .closest("table")!
64
+ .querySelectorAll(`[data-colid="${thisColId}"]`)
65
+ .forEach((elem) => elem.classList.add("qs-resultsTable-dragEnter"));
66
+ }
67
+ }
68
+
69
+ function onDragOver(ev: DragEvent) {
70
+ if (!(ev.target instanceof HTMLElement)) return;
71
+ const thisColId = ev.target.closest("th")?.dataset["colid"];
72
+ if (!thisColId || !draggingCol.current) return;
73
+
74
+ // If dragging something over a different column, allow the drop
75
+ if (draggingCol.current !== thisColId) {
76
+ ev.dataTransfer!.dropEffect = "move";
77
+ ev.preventDefault();
78
+ }
79
+ }
80
+
81
+ function onDragLeave(ev: DragEvent) {
82
+ // Remove the CSS class from the column being left
83
+ if (!(ev.target instanceof HTMLElement)) return;
84
+ const thisColId = ev.target.closest("th")?.dataset["colid"];
85
+ if (!thisColId) return;
86
+
87
+ ev.target
88
+ .closest("table")!
89
+ .querySelectorAll(`[data-colid="${thisColId}"]`)
90
+ .forEach((elem) => elem.classList.remove("qs-resultsTable-dragEnter"));
91
+ ev.preventDefault();
92
+ }
93
+
94
+ function onDrop(ev: DragEvent) {
95
+ if (!(ev.target instanceof HTMLElement)) return;
96
+ const thisColId = ev.target.closest("th")?.dataset["colid"];
97
+ if (!thisColId) return;
98
+
99
+ if (draggingCol.current) {
100
+ moveColumn(parseInt(draggingCol.current), parseInt(thisColId));
101
+ ev.preventDefault();
102
+ }
103
+ }
104
+
105
+ function onDragEnd(ev: DragEvent) {
106
+ // Called regardless of how dragging ends
107
+ // ev.target is the source element
108
+ // Just remove any dragEnter classes from cells that may remain
109
+ (ev.target as HTMLElement)
110
+ .closest("table")!
111
+ .querySelectorAll(`th, td`)
112
+ .forEach((elem) => elem.classList.remove("qs-resultsTable-dragEnter"));
113
+ draggingCol.current = "";
114
+ }
115
+
116
+ function moveColumn(oldIdx: number, newIdx: number) {
117
+ // Locate the indexes in the array where the column idx is
118
+ const arrIdxOld = showColumns.indexOf(oldIdx);
119
+ const arrIdxNew = showColumns.indexOf(newIdx);
120
+
121
+ const newColumns = [...showColumns];
122
+ const removed = newColumns.splice(arrIdxOld, 1);
123
+ newColumns.splice(arrIdxNew, 0, ...removed);
124
+ setShowColumns(newColumns);
125
+ }
126
+
127
+ function onSort(ev: MouseEvent) {
128
+ if (!(ev.currentTarget instanceof HTMLTableCellElement)) return;
129
+ const thisCol = ev.currentTarget.dataset["colid"];
130
+
131
+ if (sortColumn && thisCol === sortColumn.columnId.toString()) {
132
+ // Toggle the sort order
133
+ setSortColumn({
134
+ columnId: sortColumn.columnId,
135
+ ascending: !sortColumn.ascending,
136
+ });
137
+ } else {
138
+ // Set the sort column
139
+ setSortColumn({
140
+ columnId: parseInt(thisCol!),
141
+ ascending: true,
142
+ });
143
+ }
144
+ }
145
+
146
+ function getSortedRows(rows: CellValue[][]) {
147
+ if (!sortColumn) return rows;
148
+
149
+ const colIdx = sortColumn.columnId;
150
+ const ascending = sortColumn.ascending;
151
+
152
+ const sortedRows = [...rows];
153
+ sortedRows.sort((a, b) => {
154
+ const aVal = a[colIdx];
155
+ const bVal = b[colIdx];
156
+ if (typeof aVal === "string" && typeof bVal === "string") {
157
+ return ascending ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
158
+ } else if (typeof aVal === "number" && typeof bVal === "number") {
159
+ return ascending ? aVal - bVal : bVal - aVal;
160
+ } else if (typeof aVal === "object" && typeof bVal === "object") {
161
+ return ascending
162
+ ? aVal.sortBy - bVal.sortBy
163
+ : bVal.sortBy - aVal.sortBy;
164
+ } else {
165
+ return 0;
166
+ }
167
+ });
168
+
169
+ return sortedRows;
170
+ }
171
+
172
+ function getCellStr(col: CellValue) {
173
+ if (typeof col === "object") {
174
+ return col.value;
175
+ } else if (typeof col === "number") {
176
+ return col.toLocaleString();
177
+ } else {
178
+ return col || "";
179
+ }
180
+ }
181
+
182
+ function rowClicked(rowId: string) {
183
+ if (selectedRow === rowId && props.ensureSelected) return;
184
+
185
+ const newSelectedRow = selectedRow === rowId ? "" : rowId;
186
+ setSelectedRow(newSelectedRow);
187
+ props.onRowSelected(newSelectedRow);
188
+ }
189
+
190
+ function onClickRowMenu(ev: MouseEvent, rowid: string) {
191
+ ev.stopPropagation();
192
+ if (showRowMenu === rowid) {
193
+ setShowRowMenu("");
194
+ } else {
195
+ setShowRowMenu(rowid!);
196
+ }
197
+ }
198
+
199
+ function onClickColumnMenu(ev: MouseEvent) {
200
+ ev.stopPropagation();
201
+ setShowRowMenu("");
202
+ setShowColumnMenu(!showColumnMenu);
203
+ }
204
+
205
+ function getColumnList() {
206
+ return props.columnNames.map((name, idx) => ({
207
+ name,
208
+ idx,
209
+ show: showColumns.includes(idx),
210
+ }));
211
+ }
212
+
213
+ function toggleColumn(idx: number) {
214
+ const newColumns = [...showColumns];
215
+ const arrIdx = newColumns.indexOf(idx);
216
+ if (arrIdx === -1) {
217
+ // Not currently showing, need to add it
218
+ if (idx > newColumns.length) {
219
+ // The column position is greater than the number of columns currently showing
220
+ // So just add to the end
221
+ newColumns.push(idx);
222
+ } else {
223
+ // Insert at the correct position
224
+ newColumns.splice(idx, 0, idx);
225
+ }
226
+ } else {
227
+ newColumns.splice(arrIdx, 1);
228
+ }
229
+ setShowColumns(newColumns);
230
+ }
231
+
232
+ function deleteRow(e: MouseEvent, rowId: string) {
233
+ e.stopPropagation();
234
+ // Clear out any menus or selections for the row if needed
235
+ setShowRowMenu("");
236
+ if (selectedRow === rowId) {
237
+ setSelectedRow("");
238
+ props.onRowSelected("");
239
+ }
240
+ props.onRowDeleted(rowId);
241
+ }
242
+
243
+ return (
244
+ <table class="qs-resultsTable-sortedTable">
245
+ <thead>
246
+ <tr>
247
+ <th>
248
+ <div style="position: relative">
249
+ <svg
250
+ width="16"
251
+ height="16"
252
+ style="position: relative;"
253
+ onClick={onClickColumnMenu}
254
+ >
255
+ <rect x="1" y="3.5" width="14" height="2" fill="black" />
256
+ <rect
257
+ x="1"
258
+ y="3"
259
+ width="14"
260
+ height="12"
261
+ stroke="gray"
262
+ stroke-width="1"
263
+ fill="none"
264
+ rx="2"
265
+ />
266
+ <path
267
+ stroke="gray"
268
+ stroke-width="1"
269
+ d="M4.5,3 V15 M8,3 V15 M11.5,3 V15"
270
+ />
271
+ </svg>
272
+ <div
273
+ class={
274
+ showColumnMenu
275
+ ? "qs-resultsTable-columnMenu qs-resultsTable-showColumnMenu"
276
+ : "qs-resultsTable-columnMenu"
277
+ }
278
+ style="position: absolute; top: 16; left: 0;"
279
+ >
280
+ {getColumnList().map((elem) => (
281
+ <div
282
+ width="100px"
283
+ height="20px"
284
+ class={
285
+ elem.show
286
+ ? "qs-resultsTable-columnSelected"
287
+ : "qs-resultsTable-menuItem"
288
+ }
289
+ onClick={() => toggleColumn(elem.idx)}
290
+ >
291
+ {elem.name}
292
+ </div>
293
+ ))}
294
+ </div>
295
+ </div>
296
+ </th>
297
+ {showColumns.map((idx) => {
298
+ const isSortColumn = sortColumn?.columnId === idx;
299
+ return (
300
+ <th onClick={onSort} data-colid={idx.toString()}>
301
+ <span
302
+ class={
303
+ isSortColumn
304
+ ? "qs-resultsTable-sortHeaderCell"
305
+ : "qs-resultsTable-headerCell"
306
+ }
307
+ draggable
308
+ onDragStart={onDragStart}
309
+ onDragEnter={onDragEnter}
310
+ onDragOver={onDragOver}
311
+ onDragLeave={onDragLeave}
312
+ onDragEnd={onDragEnd}
313
+ onDrop={onDrop}
314
+ >
315
+ {props.columnNames[idx]}
316
+ </span>
317
+ {isSortColumn ? (
318
+ <svg
319
+ width="16"
320
+ height="16"
321
+ style={`transform: rotate(${
322
+ sortColumn!.ascending ? "0" : "180"
323
+ }deg)`}
324
+ >
325
+ <polygon fill="gray" points="2,10 8,4 14,10" />
326
+ </svg>
327
+ ) : null}
328
+ </th>
329
+ );
330
+ })}
331
+ </tr>
332
+ </thead>
333
+ <tbody>
334
+ {getSortedRows(props.rows).map((row) => {
335
+ const rowId = row[0].toString();
336
+ return (
337
+ <tr
338
+ onClick={() => rowClicked(rowId)}
339
+ data-rowid={rowId}
340
+ class={
341
+ rowId === selectedRow
342
+ ? "qs-resultsTable-sortedTableSelectedRow"
343
+ : undefined
344
+ }
345
+ >
346
+ <td>
347
+ <div
348
+ style="position: relative"
349
+ onClick={(e) => onClickRowMenu(e, rowId)}
350
+ >
351
+ <svg width="16" height="16" style="position: relative;">
352
+ <path
353
+ stroke-width="1.5"
354
+ stroke="gray"
355
+ stroke-linecap="round"
356
+ d="M4,5 h8 M4,8 h8 M4,11 h8"
357
+ />
358
+ </svg>
359
+ {showRowMenu === rowId ? (
360
+ <div
361
+ class="qs-resultsTable-showColumnMenu"
362
+ style="top: 16px; left: 0px;"
363
+ >
364
+ <div
365
+ class="qs-resultsTable-menuItem"
366
+ onClick={(e) => deleteRow(e, rowId)}
367
+ >
368
+ Delete
369
+ </div>
370
+ </div>
371
+ ) : null}
372
+ </div>
373
+ </td>
374
+ {showColumns.map((idx) => {
375
+ return (
376
+ <td data-colid={idx.toString()}>{getCellStr(row[idx])}</td>
377
+ );
378
+ })}
379
+ </tr>
380
+ );
381
+ })}
382
+ </tbody>
383
+ </table>
384
+ );
385
+ }
@@ -0,0 +1,148 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ import { ReData } from "./reTable.js";
5
+
6
+ function getPieSegment(
7
+ x: number,
8
+ y: number,
9
+ radius: number,
10
+ startAngle: number,
11
+ endAngle: number,
12
+ innerRadius: number,
13
+ ) {
14
+ const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
15
+ const startX = x + radius * Math.cos((Math.PI * startAngle) / 180);
16
+ const startY = y + radius * Math.sin((Math.PI * startAngle) / 180);
17
+ const endX = x + radius * Math.cos((Math.PI * endAngle) / 180);
18
+ const endY = y + radius * Math.sin((Math.PI * endAngle) / 180);
19
+ const innerStartX = x + innerRadius * Math.cos((Math.PI * startAngle) / 180);
20
+ const innerStartY = y + innerRadius * Math.sin((Math.PI * startAngle) / 180);
21
+ const innerEndX = x + innerRadius * Math.cos((Math.PI * endAngle) / 180);
22
+ const innerEndY = y + innerRadius * Math.sin((Math.PI * endAngle) / 180);
23
+ const d =
24
+ `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endX} ${endY} ` +
25
+ `L ${innerEndX} ${innerEndY} A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${innerStartX} ${innerStartY} Z`;
26
+ return d;
27
+ }
28
+
29
+ export function SpaceChart(props: { estimatesData: ReData }) {
30
+ const breakdown = props.estimatesData.physicalCounts.breakdown;
31
+
32
+ // The values to be shown on the pie chart
33
+ const physicalQubitsAlgorithm = breakdown.physicalQubitsForAlgorithm;
34
+ const physicalQubitsTFactory = breakdown.physicalQubitsForTfactories;
35
+
36
+ // TO CHECK: Divide by 0 concern here? Is there any (valid) algorithm that could
37
+ // be 0 physical qubits?
38
+ const percentQubitsAlgorithm =
39
+ physicalQubitsAlgorithm /
40
+ (physicalQubitsAlgorithm + physicalQubitsTFactory);
41
+ const breakAngleRaw = 360 * percentQubitsAlgorithm;
42
+
43
+ // The pie chart doesn't render correctly if the angle is 0 or 360
44
+ const breakAngle =
45
+ breakAngleRaw >= 360 ? 359.9 : breakAngleRaw <= 0 ? 0.1 : breakAngleRaw;
46
+
47
+ const numTFactories = breakdown.numTfactories;
48
+ const numQubitsPerTFactory = Math.round(
49
+ physicalQubitsTFactory / numTFactories,
50
+ );
51
+
52
+ return (
53
+ <div style="display: flex; flex-wrap: wrap; margin-top: 8px;">
54
+ <svg
55
+ class="qs-widget-spaceChart"
56
+ width="400"
57
+ height="400"
58
+ viewBox="50 0 450 450"
59
+ id="pieChart"
60
+ >
61
+ <path
62
+ d={getPieSegment(250, 185, 180, 0, breakAngle, 120)}
63
+ fill="var(--vscode-charts-yellow, yellow)"
64
+ stroke="white"
65
+ ></path>
66
+ <path
67
+ d={getPieSegment(250, 185, 180, breakAngle, 360, 120)}
68
+ fill="var(--vscode-charts-blue, blue)"
69
+ stroke="white"
70
+ ></path>
71
+ <text x="250" y="180" text-anchor="middle" font-size="16">
72
+ Total physical qubits
73
+ </text>
74
+ <text x="250" y="220" text-anchor="middle" font-size="32">
75
+ {props.estimatesData.physicalCountsFormatted.physicalQubits}
76
+ </text>
77
+ <rect
78
+ x="125"
79
+ y="400"
80
+ width="25"
81
+ height="25"
82
+ fill="var(--vscode-charts-yellow, yellow)"
83
+ stroke="white"
84
+ stroke-width="1"
85
+ />
86
+ <text x="155" y="408" text-anchor="start" font-size="12">
87
+ Algorithm qubits
88
+ </text>
89
+ <text x="155" y="425" text-anchor="start" font-size="16">
90
+ {physicalQubitsAlgorithm.toLocaleString()}
91
+ </text>
92
+ <rect
93
+ x="275"
94
+ y="400"
95
+ width="25"
96
+ height="25"
97
+ fill="var(--vscode-charts-blue, blue)"
98
+ stroke="white"
99
+ stroke-width="1"
100
+ />
101
+ <text x="305" y="408" text-anchor="start" font-size="12">
102
+ T factory qubits
103
+ </text>
104
+ <text x="305" y="425" text-anchor="start" font-size="16">
105
+ {physicalQubitsTFactory.toLocaleString()}
106
+ </text>
107
+ </svg>
108
+ <div class="spaceReport">
109
+ <div class="spaceReportHeader">Physical resource estimates</div>
110
+ <div class="spaceReportRow">
111
+ <div class="spaceDetailText">Total physical qubits</div>
112
+ <div>
113
+ {props.estimatesData.physicalCounts.physicalQubits.toLocaleString()}
114
+ </div>
115
+ </div>
116
+ <div class="spaceReportHeader">T factory parameters</div>
117
+ <div class="spaceReportRow">
118
+ <div class="spaceDetailText">Physical T factory qubits</div>
119
+ <div>{breakdown.physicalQubitsForTfactories.toLocaleString()}</div>
120
+ </div>
121
+ <div class="spaceReportHeader">Resource estimation breakdown</div>
122
+ <div class="spaceReportRow">
123
+ <div class="spaceDetailText">T factory copies</div>
124
+ <div>{breakdown.numTfactories.toLocaleString()}</div>
125
+ </div>
126
+ <div class="spaceReportRow">
127
+ <div class="spaceDetailText">Physical qubits per T factory</div>
128
+ <div>{numQubitsPerTFactory.toLocaleString()}</div>
129
+ </div>
130
+ <div class="spaceReportRow">
131
+ <div class="spaceDetailText">Physical algorithmic qubits</div>
132
+ <div>{physicalQubitsAlgorithm.toLocaleString()}</div>
133
+ </div>
134
+ <div class="spaceReportRow">
135
+ <div class="spaceDetailText">Logical algorithmic qubits</div>
136
+ <div>{breakdown.algorithmicLogicalQubits.toLocaleString()}</div>
137
+ </div>
138
+ <div class="spaceReportHeader">Logical qubit parameters</div>
139
+ <div class="spaceReportRow">
140
+ <div class="spaceDetailText">Physical qubits</div>
141
+ <div>
142
+ {props.estimatesData.logicalQubit.physicalQubits.toLocaleString()}
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ );
148
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "CommonJS",
4
+ "target": "ES2020",
5
+ "noEmit": true,
6
+ "lib": ["DOM", "ES2020"],
7
+ "rootDir": ".",
8
+ "strict": true /* enable all strict type-checking options */,
9
+ "jsx": "react-jsx",
10
+ "jsxImportSource": "preact",
11
+ "isolatedModules": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true
14
+ }
15
+ }