Skip to content

Commit 7fb7389

Browse files
authored
Add a return_diagnostics flag (#184)
1 parent 82883b1 commit 7fb7389

File tree

3 files changed

+68
-24
lines changed

3 files changed

+68
-24
lines changed

python_lib/qss_compiler/compile.py

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,20 @@ class CompileOptions:
9696
Most correspond directly to qss-compiler CLI arguments.
9797
"""
9898

99-
input_type: InputType = InputType.QASM3
10099
"""Input source type."""
101-
output_type: OutputType = OutputType.QEM
100+
input_type: InputType = InputType.QASM3
102101
"""Output source type."""
103-
output_file: Union[str, None] = None
102+
output_type: OutputType = OutputType.QEM
104103
"""Output file, if not supplied raw bytes will be returned."""
105-
target: Optional[str] = None
104+
output_file: Union[str, None] = None
106105
"""Hardware target to select."""
107-
config_path: Union[str, None] = None
106+
target: Optional[str] = None
108107
"""Hardware configuration location."""
109-
num_shots: Optional[int] = None
108+
config_path: Union[str, None] = None
110109
"""Number of shots to run each circuit for."""
111-
shot_delay: Optional[float] = None
110+
num_shots: Optional[int] = None
112111
"""Repetition delay between shots in seconds."""
113-
extra_args: List[str] = field(default_factory=list)
112+
shot_delay: Optional[float] = None
114113
"""Optional list of extra arguments to pass to the compiler.
115114
116115
Individual arguments must be separate arguments in the list.
@@ -121,8 +120,9 @@ class CompileOptions:
121120
resulting in args being set twice, eg., `num_shots` and `--num-shots`.
122121
This will result in an error.
123122
"""
124-
on_diagnostic: Optional[Callable[[Diagnostic], Any]] = None
123+
extra_args: List[str] = field(default_factory=list)
125124
"""Optional callback for processing diagnostic messages from the compiler."""
125+
on_diagnostic: Optional[Callable[[Diagnostic], Any]] = None
126126

127127
def prepare_compiler_option_args(self) -> List[str]:
128128
"""Prepare the compiler option arguments from this dataclass."""
@@ -168,7 +168,9 @@ def prepare_compiler_args(self) -> List[str]:
168168
args.append("--direct")
169169
args.append(str(self.input_str))
170170
else:
171-
raise exceptions.QSSCompilerNoInputError("Neither input file nor input string provided.")
171+
raise exceptions.QSSCompilerNoInputError(
172+
"Neither input file nor input string provided."
173+
)
172174

173175
return args
174176

@@ -219,7 +221,9 @@ def on_diagnostic(diag):
219221
conn.send_bytes(output)
220222

221223

222-
def _do_compile(execution: _CompilerExecution) -> Union[bytes, str, None]:
224+
def _do_compile(
225+
execution: _CompilerExecution, return_diagnostics: bool = False
226+
) -> Union[bytes, str, None]:
223227
assert (
224228
execution.input_file is not None or execution.input_str is not None
225229
), "one of the compile options input_file or input_str must be set"
@@ -249,8 +253,8 @@ def _do_compile(execution: _CompilerExecution) -> Union[bytes, str, None]:
249253
received = parent_side.recv()
250254

251255
if isinstance(received, Diagnostic):
252-
if execution.options.on_diagnostic:
253-
execution.options.on_diagnostic(received)
256+
if options.on_diagnostic:
257+
options.on_diagnostic(received)
254258
else:
255259
diagnostics.append(received)
256260
elif isinstance(received, _CompilerStatus):
@@ -262,7 +266,8 @@ def _do_compile(execution: _CompilerExecution) -> Union[bytes, str, None]:
262266
raise exceptions.QSSCompilerCommunicationFailure(
263267
"The compile process delivered an unexpected object instead of status or "
264268
"diagnostic information. This points to inconsistencies in the Python "
265-
"interface code between the calling process and the compile process."
269+
"interface code between the calling process and the compile process.",
270+
return_diagnostics=return_diagnostics,
266271
)
267272

268273
if options.output_file is None:
@@ -275,7 +280,9 @@ def _do_compile(execution: _CompilerExecution) -> Union[bytes, str, None]:
275280
childproc.kill()
276281
childproc.join()
277282
raise exceptions.QSSCompilerEOFFailure(
278-
"Compile process exited before delivering output.", diagnostics
283+
"Compile process exited before delivering output.",
284+
diagnostics,
285+
return_diagnostics=return_diagnostics,
279286
)
280287

281288
childproc.join()
@@ -287,17 +294,23 @@ def _do_compile(execution: _CompilerExecution) -> Union[bytes, str, None]:
287294
+ (" yet appears still alive" if childproc.is_alive() else "")
288295
),
289296
diagnostics,
297+
return_diagnostics=return_diagnostics,
290298
)
291299

292300
if not success:
293-
raise exceptions.QSSCompilationFailure("Failure during compilation", diagnostics)
301+
raise exceptions.QSSCompilationFailure(
302+
"Failure during compilation",
303+
diagnostics,
304+
return_diagnostics=return_diagnostics,
305+
)
294306

295307
except mp.ProcessError as e:
296308
raise exceptions.QSSCompilerError(
297309
"It's likely that you've hit a bug in the QSS Compiler. Please "
298310
"submit an issue to the team with relevant information "
299311
"(https://github.com/Qiskit/qss-compiler/issues):\n"
300-
f"{e}"
312+
f"{e}",
313+
return_diagnostics=return_diagnostics,
301314
)
302315

303316
if options.output_file is None:
@@ -321,6 +334,7 @@ def _stringify_path(p):
321334

322335
def compile_file(
323336
input_file: Union[Path, str],
337+
return_diagnostics: bool = False,
324338
compile_options: Optional[CompileOptions] = None,
325339
**kwargs,
326340
) -> Union[bytes, str, None]:
@@ -332,6 +346,7 @@ def compile_file(
332346
333347
Args:
334348
input_file: Path to the input file to compile.
349+
return_diagnostics: diagnostics visibility flag
335350
compile_options: Optional :class:`CompileOptions` dataclass.
336351
kwargs: Keywords corresponding to :class:`CompileOptions`. Ignored if `compile_options`
337352
is provided directly.
@@ -343,11 +358,12 @@ def compile_file(
343358
input_file = _stringify_path(input_file)
344359
compile_options = _prepare_compile_options(compile_options, **kwargs)
345360
execution = _CompilerExecution(input_file=input_file, options=compile_options)
346-
return _do_compile(execution)
361+
return _do_compile(execution, return_diagnostics)
347362

348363

349364
async def compile_file_async(
350365
input_file: Union[Path, str],
366+
return_diagnostics: bool = False,
351367
compile_options: Optional[CompileOptions] = None,
352368
**kwargs,
353369
) -> Union[bytes, str, None]:
@@ -358,6 +374,7 @@ async def compile_file_async(
358374
359375
Args:
360376
input_file: Path to the input file to compile.
377+
return_diagnostics: diagnostics visibility flag
361378
compile_options: Optional :class:`CompileOptions` dataclass.
362379
kwargs: Keywords corresponding to :class:`CompileOptions`. Ignored if `compile_options`
363380
is provided directly.
@@ -371,14 +388,17 @@ async def compile_file_async(
371388
execution = _CompilerExecution(input_file=input_file, options=compile_options)
372389
with ThreadPoolExecutor() as executor:
373390
loop = asyncio.get_running_loop()
374-
return await loop.run_in_executor(executor, _do_compile, execution)
391+
return await loop.run_in_executor(
392+
executor, _do_compile, execution, return_diagnostics
393+
)
375394
# As an alternative, ProcessPoolExecutor has somewhat higher overhead yet
376395
# reduces complexity of integration by not requiring the preparatory call
377396
# to set_start_method.
378397

379398

380399
def compile_str(
381400
input_str: str,
401+
return_diagnostics: bool = False,
382402
compile_options: Optional[CompileOptions] = None,
383403
**kwargs,
384404
) -> Union[bytes, str, None]:
@@ -391,6 +411,7 @@ def compile_str(
391411
392412
Args:
393413
input_str: input to compile as string (e.q., an OpenQASM3 program).
414+
return_diagnostics: diagnostics visibility flag
394415
compile_options: Optional :class:`CompileOptions` dataclass.
395416
kwargs: Keywords corresponding to :class:`CompileOptions`. Ignored if `compile_options`
396417
is provided directly.
@@ -401,11 +422,12 @@ def compile_str(
401422
"""
402423
compile_options = _prepare_compile_options(compile_options, **kwargs)
403424
execution = _CompilerExecution(input_str=input_str, options=compile_options)
404-
return _do_compile(execution)
425+
return _do_compile(execution, return_diagnostics=return_diagnostics)
405426

406427

407428
async def compile_str_async(
408429
input_str: str,
430+
return_diagnostics: bool = False,
409431
compile_options: Optional[CompileOptions] = None,
410432
**kwargs,
411433
) -> Union[bytes, str, None]:
@@ -416,6 +438,7 @@ async def compile_str_async(
416438
417439
Args:
418440
input_str: input to compile as string (e.q., an OpenQASM3 program).
441+
return_diagnostics: diagnostics visibility flag
419442
compile_options: Optional :class:`CompileOptions` dataclass.
420443
kwargs: Keywords corresponding to :class:`CompileOptions`. Ignored if `compile_options`
421444
is provided directly.
@@ -429,7 +452,9 @@ async def compile_str_async(
429452
execution = _CompilerExecution(input_str=input_str, options=compile_options)
430453
with ThreadPoolExecutor() as executor:
431454
loop = asyncio.get_running_loop()
432-
return await loop.run_in_executor(executor, _do_compile, execution)
455+
return await loop.run_in_executor(
456+
executor, _do_compile, execution, return_diagnostics
457+
)
433458
# As an alternative, ProcessPoolExecutor has somewhat higher overhead yet
434459
# reduces complexity of integration by not requiring the preparatory call
435460
# to set_start_method.

python_lib/qss_compiler/exceptions.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,22 @@ class QSSCompilerError(Exception):
3131
"""Raised on errors invoking the compiler or when the interaction between
3232
Python interface and native backend code fails."""
3333

34-
def __init__(self, message: str, diagnostics: Optional[List[Diagnostic]] = None):
34+
def __init__(
35+
self,
36+
message: str,
37+
diagnostics: Optional[List[Diagnostic]] = None,
38+
return_diagnostics: bool = False,
39+
):
3540
"""Set the error message."""
3641
self.message = message
3742
self.diagnostics = [] if diagnostics is None else diagnostics
43+
self.return_diagnostics = return_diagnostics
3844

3945
def __str__(self):
4046
"""Return the message."""
41-
return "\n".join([self.message, _diagnostics_to_str(self.diagnostics)])
47+
if self.return_diagnostics:
48+
return "\n".join([self.message, _diagnostics_to_str(self.diagnostics)])
49+
return "\n".join([self.message])
4250

4351

4452
class QSSCompilerNoInputError(QSSCompilerError):
@@ -60,33 +68,42 @@ class QSSCompilerNonZeroStatus(QSSCompilerError):
6068
class QSSCompilationFailure(QSSCompilerError):
6169
"""Raised during other compilation failure."""
6270

71+
6372
class QSSLinkingFailure(QSSCompilerError):
6473
"""Raised on linking failure."""
6574

75+
6676
class QSSLinkerNotImplemented(QSSCompilerError):
6777
"""Raised on linking failure."""
6878

79+
6980
class QSSArgumentInputTypeError(QSSLinkingFailure):
7081
"""Raised when argument type is invalid"""
7182

83+
7284
class QSSLinkSignatureError(QSSLinkingFailure):
7385
"""Raised when signature file format is invalid"""
7486

87+
7588
class QSSLinkSignatureWarning(QSSLinkingFailure, Warning):
7689
"""Raised when signature file format is invalid but may still be processed"""
7790

91+
7892
class QSSLinkAddressError(QSSLinkingFailure):
7993
"""Raised when signature link address is invalid"""
8094

95+
8196
class QSSLinkSignatureNotFound(QSSLinkingFailure):
8297
"""Raised when argument signature file is not found"""
8398

99+
84100
class QSSLinkArgumentNotFoundWarning(QSSLinkingFailure, Warning):
85101
"""Raised when parameter name in signature is not found in arguments"""
86102

103+
87104
class QSSLinkInvalidPatchTypeError(QSSLinkingFailure):
88105
"""Raised when parameter patch type is invalid"""
89106

107+
90108
class QSSLinkInvalidArgumentError(QSSLinkingFailure):
91109
"""Raised when argument is invalid"""
92-

test/python_lib/test_compile.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def test_compile_invalid_file(example_invalid_qasm3_tmpfile):
101101
with pytest.raises(QSSCompilationFailure):
102102
compile_file(
103103
example_invalid_qasm3_tmpfile,
104+
return_diagnostics=True, # For testing purposes
104105
input_type=InputType.QASM3,
105106
output_type=OutputType.MLIR,
106107
output_file=None,
@@ -114,6 +115,7 @@ def test_compile_invalid_str(example_invalid_qasm3_str):
114115
with pytest.raises(QSSCompilationFailure) as compfail:
115116
compile_str(
116117
example_invalid_qasm3_str,
118+
return_diagnostics=True, # For testing purposes
117119
input_type=InputType.QASM3,
118120
output_type=OutputType.MLIR,
119121
output_file=None,

0 commit comments

Comments
 (0)