microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.20.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

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