microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billt/mac-intel-cryptography

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 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(program: ProgramConfig): Promise<string>;
41
42 getHir(program: ProgramConfig): 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(program: ProgramConfig): Promise<string> {
152 const config = toWasmProgramConfig(program, "unrestricted");
153 return callAndTransformExceptions(async () => this.wasm.get_ast(config));
154 }
155
156 async getHir(program: ProgramConfig): Promise<string> {
157 const config = toWasmProgramConfig(program, "unrestricted");
158 return callAndTransformExceptions(async () => this.wasm.get_hir(config));
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