microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fedimser/memory-re

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

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