microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.23.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/npm/qsharp/src/compiler/compiler.ts

364lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4import {
5 CURRENT_VERSION,
6 type CircuitGroup as CircuitData,
7} from "../data-structures/circuit.js";
8import {
9 IDocFile,
10 IOperationInfo,
11 ICircuitConfig,
12 IPackageGraphSources,
13 IProgramConfig as wasmIProgramConfig,
14 TargetProfile,
15 type VSDiagnostic,
16 ProjectType,
17} from "../../lib/web/qsc_wasm.js";
18import { log } from "../log.js";
19import {
20 IServiceProxy,
21 ServiceProtocol,
22 ServiceState,
23} from "../workers/common.js";
24import { eventStringToMsg } from "./common.js";
25import {
26 IQscEventTarget,
27 QscEventData,
28 QscEvents,
29 makeEvent,
30} from "./events.js";
31import { callAndTransformExceptions } from "../diagnostics.js";
32
33// The wasm types generated for the node.js bundle are just the exported APIs,
34// so use those as the set used by the shared compiler
35type Wasm = typeof import("../../lib/web/qsc_wasm.js");
36
37// These need to be async/promise results for when communicating across a WebWorker, however
38// for running the compiler in the same thread the result will be synchronous (a resolved promise).
39export interface ICompiler {
40 checkCode(code: string): Promise<VSDiagnostic[]>;
41
42 getAst(code: string, languageFeatures: string[]): Promise<string>;
43
44 getHir(code: string, languageFeatures: string[]): Promise<string>;
45
46 getRir(program: ProgramConfig): Promise<string[]>;
47
48 run(
49 program: ProgramConfig,
50 expr: string,
51 shots: number,
52 eventHandler: IQscEventTarget,
53 ): Promise<void>;
54
55 runWithNoise(
56 program: ProgramConfig,
57 expr: string,
58 shots: number,
59 pauliNoise: number[],
60 qubitLoss: number,
61 eventHandler: IQscEventTarget,
62 ): Promise<void>;
63
64 getQir(program: ProgramConfig): Promise<string>;
65
66 getEstimates(
67 program: ProgramConfig,
68 expr: string,
69 params: string,
70 ): Promise<string>;
71
72 getCircuit(
73 program: ProgramConfig,
74 config: ICircuitConfig,
75 operation?: IOperationInfo,
76 ): Promise<CircuitData>;
77
78 getDocumentation(additionalProgram?: ProgramConfig): Promise<IDocFile[]>;
79
80 getLibrarySummaries(): Promise<string>;
81
82 checkExerciseSolution(
83 userCode: string,
84 exerciseSources: string[],
85 eventHandler: IQscEventTarget,
86 ): Promise<boolean>;
87}
88
89/**
90 * Type definition for the configuration of a program.
91 * If adding new properties, make them optional to maintain backward compatibility.
92 */
93export type ProgramConfig = (
94 | {
95 /** An array of source objects, each containing a name and contents. */
96 sources: [string, string][];
97 /** An array of language features to be opted in to in this compilation. */
98 languageFeatures: string[];
99 }
100 | {
101 /** Sources from all resolved dependencies, along with their languageFeatures configuration */
102 packageGraphSources: IPackageGraphSources;
103 }
104) & {
105 /** Target compilation profile. */
106 profile?: TargetProfile;
107 /** The type of project. This is used to determine how to load the project. */
108 projectType?: ProjectType;
109};
110
111// WebWorker also support being explicitly terminated to tear down the worker thread
112export type ICompilerWorker = ICompiler & IServiceProxy;
113export type CompilerState = ServiceState;
114
115export class Compiler implements ICompiler {
116 private wasm: Wasm;
117
118 constructor(wasm: Wasm) {
119 log.info("Constructing a Compiler instance");
120 this.wasm = wasm;
121 globalThis.qscGitHash = this.wasm.git_hash();
122 }
123
124 // Note: This function does not support project mode.
125 // see https://github.com/microsoft/qsharp/pull/849#discussion_r1409821143
126 async checkCode(code: string): Promise<VSDiagnostic[]> {
127 let diags: VSDiagnostic[] = [];
128 const languageService = new this.wasm.LanguageService();
129 const work = languageService.start_background_work(
130 (uri: string, version: number | undefined, errors: VSDiagnostic[]) => {
131 diags = errors;
132 },
133 () => {
134 // do nothing; test callables are not reported in checkCode
135 },
136 {
137 readFile: async () => null,
138 listDirectory: async () => [],
139 resolvePath: async () => null,
140 fetchGithub: async () => "",
141 findManifestDirectory: async () => null,
142 },
143 );
144 languageService.update_document("code", 1, code, "qsharp");
145 // Yield to let the language service background worker handle the update
146 await Promise.resolve();
147 languageService.stop_background_work();
148 await work;
149 languageService.free();
150 return diags;
151 }
152
153 async getAst(code: string, languageFeatures: string[]): Promise<string> {
154 return this.wasm.get_ast(code, languageFeatures);
155 }
156
157 async getHir(code: string, languageFeatures: string[]): Promise<string> {
158 return this.wasm.get_hir(code, languageFeatures);
159 }
160
161 async getRir(program: ProgramConfig): Promise<string[]> {
162 const config = toWasmProgramConfig(program, "adaptive_ri");
163 return callAndTransformExceptions(async () => this.wasm.get_rir(config));
164 }
165
166 async run(
167 program: ProgramConfig,
168 expr: string,
169 shots: number,
170 eventHandler: IQscEventTarget,
171 ): Promise<void> {
172 // All results are communicated as events, but if there is a compiler error (e.g. an invalid
173 // entry expression or similar), it may throw on run. The caller should expect this promise
174 // may reject without all shots running or events firing.
175 await callAndTransformExceptions(async () =>
176 this.wasm.run(
177 toWasmProgramConfig(program, "unrestricted"),
178 expr,
179 (msg: string) => onCompilerEvent(msg, eventHandler!),
180 shots!,
181 ),
182 );
183 }
184
185 async runWithNoise(
186 program: ProgramConfig,
187 expr: string,
188 shots: number,
189 pauliNoise: number[],
190 qubitLoss: number,
191 eventHandler: IQscEventTarget,
192 ): Promise<void> {
193 await callAndTransformExceptions(async () =>
194 this.wasm.runWithNoise(
195 toWasmProgramConfig(program, "unrestricted"),
196 expr,
197 (msg: string) => onCompilerEvent(msg, eventHandler!),
198 shots!,
199 pauliNoise,
200 qubitLoss,
201 ),
202 );
203 }
204
205 async getQir(program: ProgramConfig): Promise<string> {
206 return callAndTransformExceptions(async () =>
207 this.wasm.get_qir(toWasmProgramConfig(program, "base")),
208 );
209 }
210
211 async getEstimates(
212 program: ProgramConfig,
213 expr: string,
214 params: string,
215 ): Promise<string> {
216 return callAndTransformExceptions(async () =>
217 this.wasm.get_estimates(
218 toWasmProgramConfig(program, "unrestricted"),
219 expr,
220 params,
221 ),
222 );
223 }
224
225 async getCircuit(
226 program: ProgramConfig,
227 config: ICircuitConfig,
228 operation?: IOperationInfo,
229 ): Promise<CircuitData> {
230 const circuit = await callAndTransformExceptions(async () =>
231 this.wasm.get_circuit(
232 toWasmProgramConfig(program, "unrestricted"),
233 operation,
234 config,
235 ),
236 );
237 return {
238 circuits: [circuit],
239 version: CURRENT_VERSION,
240 };
241 }
242
243 // Returns all autogenerated documentation files for the standard library
244 // and loaded project (if requested). This include file names and metadata,
245 // including specially formatted table of content file.
246 async getDocumentation(
247 additionalProgram?: ProgramConfig,
248 ): Promise<IDocFile[]> {
249 return this.wasm.generate_docs(
250 additionalProgram &&
251 toWasmProgramConfig(additionalProgram, "unrestricted"),
252 );
253 }
254
255 async getLibrarySummaries(): Promise<string> {
256 return this.wasm.get_library_summaries();
257 }
258
259 async checkExerciseSolution(
260 userCode: string,
261 exerciseSources: string[],
262 eventHandler: IQscEventTarget,
263 ): Promise<boolean> {
264 const success = this.wasm.check_exercise_solution(
265 userCode,
266 exerciseSources,
267 (msg: string) => onCompilerEvent(msg, eventHandler),
268 );
269
270 return success;
271 }
272}
273
274/**
275 * Fills in the defaults, to convert from the backwards-compatible ProgramConfig,
276 * to the IProgramConfig type that the wasm layer expects
277 */
278export function toWasmProgramConfig(
279 program: ProgramConfig,
280 defaultProfile: TargetProfile,
281): Required<wasmIProgramConfig> {
282 let packageGraphSources: IPackageGraphSources;
283
284 if ("sources" in program) {
285 // The simpler type is used, where there are no dependencies and only a list
286 // of sources is passed in.
287 packageGraphSources = {
288 root: {
289 sources: program.sources,
290 languageFeatures: program.languageFeatures || [],
291 dependencies: {},
292 },
293 packages: {},
294 hasManifest: false, // "sources" is only used in scenarios where there is no manifest
295 };
296 } else {
297 // A full package graph is passed in.
298 packageGraphSources = program.packageGraphSources;
299 }
300
301 return {
302 packageGraphSources,
303 profile: program.profile || defaultProfile,
304 projectType: program.projectType || "qsharp",
305 };
306}
307
308export function onCompilerEvent(msg: string, eventTarget: IQscEventTarget) {
309 const qscMsg = eventStringToMsg(msg);
310 if (!qscMsg) {
311 log.error("Unknown event message: %s", msg);
312 return;
313 }
314
315 let qscEvent: QscEvents;
316
317 const msgType = qscMsg.type;
318 switch (msgType) {
319 case "Message":
320 qscEvent = makeEvent("Message", qscMsg.message);
321 break;
322 case "DumpMachine":
323 qscEvent = makeEvent("DumpMachine", {
324 state: qscMsg.state,
325 stateLatex: qscMsg.stateLatex,
326 qubitCount: qscMsg.qubitCount,
327 });
328 break;
329 case "Result":
330 qscEvent = makeEvent("Result", qscMsg.result);
331 break;
332 case "Matrix":
333 qscEvent = makeEvent("Matrix", {
334 matrix: qscMsg.matrix,
335 matrixLatex: qscMsg.matrixLatex,
336 });
337 break;
338 default:
339 log.never(msgType);
340 throw "Unexpected message type";
341 }
342 log.debug("worker dispatching event " + JSON.stringify(qscEvent));
343 eventTarget.dispatchEvent(qscEvent);
344}
345
346/** The protocol definition to allow running the compiler in a worker. */
347export const compilerProtocol: ServiceProtocol<ICompiler, QscEventData> = {
348 class: Compiler,
349 methods: {
350 checkCode: "request",
351 getAst: "request",
352 getHir: "request",
353 getRir: "request",
354 getQir: "request",
355 getEstimates: "request",
356 getCircuit: "request",
357 getDocumentation: "request",
358 getLibrarySummaries: "request",
359 run: "requestWithProgress",
360 runWithNoise: "requestWithProgress",
361 checkExerciseSolution: "requestWithProgress",
362 },
363 eventNames: ["DumpMachine", "Matrix", "Message", "Result"],
364};
365