Skip to content

Commit 3b8abfd

Browse files
minestarksbillti
andauthored
Remove debug with circuit menu item; more helpful handling for result comparison error (#1323)
More UX improvements and an attempt to be more helpful with known error conditions. ## "Show circuit" command (or Code Lens) ![image](https://github.com/microsoft/qsharp/assets/16928427/b30b79e3-2d9f-444a-b203-d7b4043d52c8) ## "Run file and show circuit diagram" command (i.e. running in simulator) ![image](https://github.com/microsoft/qsharp/assets/16928427/787f925c-5a75-4d16-af9d-9af06069c105) ## Program that compares measurement results, Target Profile = Unrestricted ![image](https://github.com/microsoft/qsharp/assets/16928427/b40dd857-3452-4cd8-ac72-1d08ecef298b) ## Same program, but with "Run file and show circuit diagram" ![image](https://github.com/microsoft/qsharp/assets/16928427/5674be51-6f73-4770-8ad5-b00c17797755) ## Same program, but with Target Profile = Base ![image](https://github.com/microsoft/qsharp/assets/16928427/3a9af968-75fd-4760-80b1-3d372ac27b55) --------- Co-authored-by: Bill Ticehurst <billti@microsoft.com>
1 parent 38a0d44 commit 3b8abfd

File tree

8 files changed

+183
-129
lines changed

8 files changed

+183
-129
lines changed

npm/ux/circuit.tsx

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import * as qviz from "@microsoft/quantum-viz.js/lib";
55
import { useEffect, useRef } from "preact/hooks";
6+
import { CircuitProps } from "./data.js";
67

78
// For perf reasons we set a limit on how many gates/qubits
89
// we attempt to render. This is still a lot higher than a human would
@@ -19,12 +20,6 @@ export function Circuit(props: { circuit: qviz.Circuit }) {
1920
props.circuit.qubits.length === 0 ? (
2021
<div>
2122
<p>No circuit to display. No qubits have been allocated.</p>
22-
<p>
23-
<em>
24-
Tip: you can generate a circuit diagram for any operation that takes
25-
qubits or arrays of qubits as input.
26-
</em>
27-
</p>
2823
</div>
2924
) : props.circuit.operations.length > MAX_OPERATIONS ? (
3025
<div>
@@ -67,31 +62,52 @@ export function Circuit(props: { circuit: qviz.Circuit }) {
6762
}
6863

6964
/* This component is exclusive to the VS Code panel */
70-
export function CircuitPanel(props: {
71-
title: string;
72-
subtitle: string;
73-
circuit?: qviz.Circuit;
74-
errorHtml?: string;
75-
}) {
65+
export function CircuitPanel(props: CircuitProps) {
66+
const error = props.errorHtml ? (
67+
<div>
68+
<p>
69+
{props.circuit
70+
? "The program encountered a failure. See the error(s) below."
71+
: "A circuit could not be generated for this program. See the error(s) below."}
72+
<br />
73+
</p>
74+
<div dangerouslySetInnerHTML={{ __html: props.errorHtml }}></div>
75+
</div>
76+
) : null;
77+
7678
return (
7779
<div class="qs-circuit-panel">
7880
<div>
7981
<h1>{props.title}</h1>
8082
</div>
8183
{props.circuit ? <Circuit circuit={props.circuit}></Circuit> : null}
82-
<div class="qs-circuit-error">
83-
{props.errorHtml ? (
84-
<div>
85-
<p>
86-
A circuit could not be generated for this program. See the
87-
error(s) below.
88-
<br />
89-
</p>
90-
<div dangerouslySetInnerHTML={{ __html: props.errorHtml }}></div>
91-
</div>
92-
) : null}
93-
</div>
94-
<p>{props.subtitle /* target profile */}</p>
84+
<div class="qs-circuit-error">{error}</div>
85+
<p>{props.targetProfile}</p>
86+
{props.simulating ? (
87+
<p>
88+
This circuit diagram was generated while running the program in the
89+
simulator.
90+
<br />
91+
<br />
92+
If your program contains behavior that is conditional on a qubit
93+
measurement result, note that this circuit only shows the outcome that
94+
was encountered during this simulation. Running the program again may
95+
result in a different circuit being generated.
96+
</p>
97+
) : null}
98+
{
99+
// show tip when the circuit is empty and we didn't run under the simulator (i.e. debugging)
100+
!props.simulating &&
101+
!props.errorHtml &&
102+
props.circuit?.qubits.length === 0 ? (
103+
<p>
104+
<em>
105+
Tip: you can generate a circuit diagram for any operation that
106+
takes qubits or arrays of qubits as input.
107+
</em>
108+
</p>
109+
) : null
110+
}
95111
<p>
96112
<a href="https://github.com/microsoft/qsharp/wiki/Circuit-Diagrams-from-Q%23-Code">
97113
Learn more
@@ -100,5 +116,3 @@ export function CircuitPanel(props: {
100116
</div>
101117
);
102118
}
103-
104-
export type CircuitData = qviz.Circuit;

npm/ux/data.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,13 @@ export function CreateSingleEstimateResult(
6363
};
6464
}
6565
}
66+
67+
export type CircuitProps = {
68+
title: string;
69+
circuit?: CircuitData;
70+
errorHtml?: string;
71+
targetProfile: string;
72+
simulating: boolean;
73+
};
74+
75+
export type CircuitData = import("@microsoft/quantum-viz.js/lib").Circuit;

npm/ux/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
import "./qsharp-ux.css";
77
import "./qsharp-circuit.css";
88

9-
export { CreateSingleEstimateResult, type ReData } from "./data.js";
9+
export {
10+
CreateSingleEstimateResult,
11+
type ReData,
12+
type CircuitData,
13+
type CircuitProps,
14+
} from "./data.js";
1015
export { Histogram } from "./histogram.js";
1116
export { ReTable } from "./reTable.js";
1217
export { SpaceChart } from "./spaceChart.js";
1318
export { ScatterChart } from "./scatterChart.js";
1419
export { EstimatesOverview } from "./estimatesOverview.js";
1520
export { EstimatesPanel } from "./estimatesPanel.js";
16-
export { Circuit, CircuitPanel, type CircuitData } from "./circuit.js";
21+
export { Circuit, CircuitPanel } from "./circuit.js";

vscode/package.json

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,6 @@
132132
"command": "qsharp-vscode.debugEditorContents",
133133
"when": "resourceLangId == qsharp",
134134
"group": "navigation@2"
135-
},
136-
{
137-
"command": "qsharp-vscode.debugEditorContentsWithCircuit",
138-
"when": "resourceLangId == qsharp",
139-
"group": "navigation@2"
140135
}
141136
],
142137
"commandPalette": [
@@ -149,7 +144,7 @@
149144
"when": "resourceLangId == qsharp"
150145
},
151146
{
152-
"command": "qsharp-vscode.debugEditorContentsWithCircuit",
147+
"command": "qsharp-vscode.runEditorContentsWithCircuit",
153148
"when": "resourceLangId == qsharp"
154149
},
155150
{
@@ -285,11 +280,9 @@
285280
"icon": "$(play)"
286281
},
287282
{
288-
"command": "qsharp-vscode.debugEditorContentsWithCircuit",
289-
"title": "Debug Q# File with Circuit Diagram",
290-
"category": "Debug",
291-
"enablement": "!inDebugMode",
292-
"icon": "$(debug-alt)"
283+
"command": "qsharp-vscode.runEditorContentsWithCircuit",
284+
"title": "Run file and show circuit diagram",
285+
"category": "Q#"
293286
},
294287
{
295288
"command": "qsharp-vscode.showHistogram",

vscode/src/circuit.ts

Lines changed: 70 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
import { type Circuit as CircuitData } from "@microsoft/quantum-viz.js/lib/circuit.js";
4+
import { type Circuit as CircuitData } from "@microsoft/quantum-viz.js/lib";
5+
import { escapeHtml } from "markdown-it/lib/common/utils";
56
import {
67
IOperationInfo,
78
IRange,
@@ -52,7 +53,9 @@ export async function showCircuitCommand(
5253
log.info("terminating circuit worker due to timeout");
5354
worker.terminate();
5455
}, compilerRunTimeoutMs);
55-
const sources = await loadProject(editor.document.uri);
56+
57+
const docUri = editor.document.uri;
58+
const sources = await loadProject(docUri);
5659
const targetProfile = getTarget();
5760

5861
try {
@@ -63,10 +66,9 @@ export async function showCircuitCommand(
6366

6467
updateCircuitPanel(
6568
targetProfile,
66-
editor.document.uri.path,
67-
circuit,
69+
docUri.path,
6870
true, // reveal
69-
operation,
71+
{ circuit, operation },
7072
);
7173

7274
sendTelemetryEvent(EventType.CircuitEnd, {
@@ -81,7 +83,7 @@ export async function showCircuitCommand(
8183
typeof e === "string" ? JSON.parse(e) : undefined;
8284
let errorHtml = "There was an error generating the circuit.";
8385
if (errors) {
84-
errorHtml = errorsToHtml(errors);
86+
errorHtml = errorsToHtml(errors, docUri);
8587
}
8688

8789
if (!timeout) {
@@ -94,46 +96,16 @@ export async function showCircuitCommand(
9496

9597
updateCircuitPanel(
9698
targetProfile,
97-
editor.document.uri.path,
98-
errorHtml,
99+
docUri.path,
99100
false, // reveal
100-
operation,
101+
{ errorHtml, operation },
101102
);
102103
} finally {
103104
log.info("terminating circuit worker");
104105
worker.terminate();
105106
}
106107
}
107108

108-
export function updateCircuitPanel(
109-
targetProfile: string,
110-
docPath: string,
111-
circuitOrErrorHtml: CircuitData | string,
112-
reveal: boolean,
113-
operation?: IOperationInfo | undefined,
114-
) {
115-
let title;
116-
let subtitle;
117-
if (operation) {
118-
title = `${operation.operation} with ${operation.totalNumQubits} input qubits`;
119-
subtitle = `Target profile: ${getTargetFriendlyName(targetProfile)} `;
120-
} else {
121-
title = basename(docPath) || "Circuit";
122-
subtitle = `Target profile: ${getTargetFriendlyName(targetProfile)}`;
123-
}
124-
125-
const message = {
126-
command: "circuit",
127-
title,
128-
subtitle,
129-
circuit:
130-
typeof circuitOrErrorHtml === "object" ? circuitOrErrorHtml : undefined,
131-
errorHtml:
132-
typeof circuitOrErrorHtml === "string" ? circuitOrErrorHtml : undefined,
133-
};
134-
sendMessageToPanel("circuit", reveal, message);
135-
}
136-
137109
/**
138110
* Formats an array of compiler/runtime errors into HTML to be presented to the user.
139111
*
@@ -145,20 +117,37 @@ export function updateCircuitPanel(
145117
* @returns The HTML formatted errors, to be set as the inner contents of a container element.
146118
*/
147119
function errorsToHtml(
148-
errors: [string, VSDiagnostic, string | undefined][],
149-
): string {
120+
errors: [string, VSDiagnostic, string][],
121+
programUri: Uri,
122+
) {
150123
let errorHtml = "";
151124
for (const error of errors) {
152125
const [document, diag, rawStack] = error;
153126

154-
const location = documentHtml(document, diag.range);
155-
156-
const message = escapeHtml(`(${diag.code}) ${diag.message}`).replace(
157-
"\n",
158-
"<br/><br/>",
159-
);
160-
161-
errorHtml += `<p>${location}: ${message}<br/></p>`;
127+
if (diag.code === "Qsc.Eval.ResultComparisonUnsupported") {
128+
const commandUri = Uri.parse(
129+
`command:qsharp-vscode.runEditorContentsWithCircuit?${encodeURIComponent(JSON.stringify([programUri]))}`,
130+
true,
131+
);
132+
const messageHtml =
133+
`<p>Synthesizing circuits is unsupported for programs that ` +
134+
`contain behavior that is conditional on a qubit measurement result, ` +
135+
`since the resulting circuit may depend on the outcome of the measurement.</p>` +
136+
`<p>If you would like to generate a circuit for this program, you can ` +
137+
`<a href="${commandUri}">run the program in the simulator and show the resulting circuit</a>, ` +
138+
`or edit your code to avoid the result comparison indicated by the call stack below.</p>`;
139+
140+
errorHtml += messageHtml;
141+
} else {
142+
const location = documentHtml(document, diag.range);
143+
144+
const message = escapeHtml(`(${diag.code}) ${diag.message}`).replace(
145+
"\n",
146+
"<br/><br/>",
147+
);
148+
149+
errorHtml += `<p>${location}: ${message}<br/></p>`;
150+
}
162151

163152
if (rawStack) {
164153
const stack = rawStack
@@ -181,6 +170,39 @@ function errorsToHtml(
181170
return errorHtml;
182171
}
183172

173+
export function updateCircuitPanel(
174+
targetProfile: string,
175+
docPath: string,
176+
reveal: boolean,
177+
params: {
178+
circuit?: CircuitData;
179+
errorHtml?: string;
180+
simulating?: boolean;
181+
operation?: IOperationInfo | undefined;
182+
},
183+
) {
184+
const title = params?.operation
185+
? `${params.operation.operation} with ${params.operation.totalNumQubits} input qubits`
186+
: basename(docPath) || "Circuit";
187+
188+
// Trim the Q#: prefix from the target profile name - that's meant for the ui text in the status bar
189+
const target = `Target profile: ${getTargetFriendlyName(targetProfile).replace("Q#: ", "")} `;
190+
191+
const props = {
192+
title,
193+
targetProfile: target,
194+
simulating: params?.simulating || false,
195+
circuit: params?.circuit,
196+
errorHtml: params?.errorHtml,
197+
};
198+
199+
const message = {
200+
command: "circuit",
201+
props,
202+
};
203+
sendMessageToPanel("circuit", reveal, message);
204+
}
205+
184206
/**
185207
* If the input is a URI, turns it into a document open link.
186208
* Otherwise returns the HTML-escaped input
@@ -218,12 +240,3 @@ function documentHtml(maybeUri: string, range?: IRange) {
218240

219241
return location;
220242
}
221-
222-
function escapeHtml(unsafe: string): string {
223-
return unsafe
224-
.replace(/&/g, "&amp;")
225-
.replace(/</g, "&lt;")
226-
.replace(/>/g, "&gt;")
227-
.replace(/"/g, "&quot;")
228-
.replace(/'/g, "&#039;");
229-
}

0 commit comments

Comments
 (0)