microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/qasm-playground

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

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