microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
minestarks/circuit-folding

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

365lines · 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 log.debug("config passed to getCircuit: ", config);
231 const circuit = await callAndTransformExceptions(async () =>
232 this.wasm.get_circuit(
233 toWasmProgramConfig(program, "unrestricted"),
234 operation,
235 config,
236 ),
237 );
238 return {
239 circuits: [circuit],
240 version: CURRENT_VERSION,
241 };
242 }
243
244 // Returns all autogenerated documentation files for the standard library
245 // and loaded project (if requested). This include file names and metadata,
246 // including specially formatted table of content file.
247 async getDocumentation(
248 additionalProgram?: ProgramConfig,
249 ): Promise<IDocFile[]> {
250 return this.wasm.generate_docs(
251 additionalProgram &&
252 toWasmProgramConfig(additionalProgram, "unrestricted"),
253 );
254 }
255
256 async getLibrarySummaries(): Promise<string> {
257 return this.wasm.get_library_summaries();
258 }
259
260 async checkExerciseSolution(
261 userCode: string,
262 exerciseSources: string[],
263 eventHandler: IQscEventTarget,
264 ): Promise<boolean> {
265 const success = this.wasm.check_exercise_solution(
266 userCode,
267 exerciseSources,
268 (msg: string) => onCompilerEvent(msg, eventHandler),
269 );
270
271 return success;
272 }
273}
274
275/**
276 * Fills in the defaults, to convert from the backwards-compatible ProgramConfig,
277 * to the IProgramConfig type that the wasm layer expects
278 */
279export function toWasmProgramConfig(
280 program: ProgramConfig,
281 defaultProfile: TargetProfile,
282): Required<wasmIProgramConfig> {
283 let packageGraphSources: IPackageGraphSources;
284
285 if ("sources" in program) {
286 // The simpler type is used, where there are no dependencies and only a list
287 // of sources is passed in.
288 packageGraphSources = {
289 root: {
290 sources: program.sources,
291 languageFeatures: program.languageFeatures || [],
292 dependencies: {},
293 },
294 packages: {},
295 hasManifest: false, // "sources" is only used in scenarios where there is no manifest
296 };
297 } else {
298 // A full package graph is passed in.
299 packageGraphSources = program.packageGraphSources;
300 }
301
302 return {
303 packageGraphSources,
304 profile: program.profile || defaultProfile,
305 projectType: program.projectType || "qsharp",
306 };
307}
308
309export function onCompilerEvent(msg: string, eventTarget: IQscEventTarget) {
310 const qscMsg = eventStringToMsg(msg);
311 if (!qscMsg) {
312 log.error("Unknown event message: %s", msg);
313 return;
314 }
315
316 let qscEvent: QscEvents;
317
318 const msgType = qscMsg.type;
319 switch (msgType) {
320 case "Message":
321 qscEvent = makeEvent("Message", qscMsg.message);
322 break;
323 case "DumpMachine":
324 qscEvent = makeEvent("DumpMachine", {
325 state: qscMsg.state,
326 stateLatex: qscMsg.stateLatex,
327 qubitCount: qscMsg.qubitCount,
328 });
329 break;
330 case "Result":
331 qscEvent = makeEvent("Result", qscMsg.result);
332 break;
333 case "Matrix":
334 qscEvent = makeEvent("Matrix", {
335 matrix: qscMsg.matrix,
336 matrixLatex: qscMsg.matrixLatex,
337 });
338 break;
339 default:
340 log.never(msgType);
341 throw "Unexpected message type";
342 }
343 log.debug("worker dispatching event " + JSON.stringify(qscEvent));
344 eventTarget.dispatchEvent(qscEvent);
345}
346
347/** The protocol definition to allow running the compiler in a worker. */
348export const compilerProtocol: ServiceProtocol<ICompiler, QscEventData> = {
349 class: Compiler,
350 methods: {
351 checkCode: "request",
352 getAst: "request",
353 getHir: "request",
354 getRir: "request",
355 getQir: "request",
356 getEstimates: "request",
357 getCircuit: "request",
358 getDocumentation: "request",
359 getLibrarySummaries: "request",
360 run: "requestWithProgress",
361 runWithNoise: "requestWithProgress",
362 checkExerciseSolution: "requestWithProgress",
363 },
364 eventNames: ["DumpMachine", "Matrix", "Message", "Result"],
365};
366