-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Implement built-in state_result
function
#905
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #905 +/- ##
==========================================
+ Coverage 93.12% 93.16% +0.04%
==========================================
Files 84 86 +2
Lines 9701 9775 +74
==========================================
+ Hits 9034 9107 +73
- Misses 667 668 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
guppylang/compiler/expr_compiler.py
Outdated
) | ||
|
||
qubits_in = [self.visit(e) for e in node.args[1:]] | ||
# TODO: This should be caught during checking. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Importing qubit
for the StateResultChecker
in checker.py
leads to circular import errors - not sure if there is an obvious other place to move it to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that qubit
is a library defined type and not specifically known by the compiler (unlike the types in builtins
).
You would need to do something like the following:
class StateResultChecker(CustomCallChecker):
"""Call checker for the `state_result` function."""
def synthesize(self, args: list[ast.expr]) -> tuple[ast.expr, Type]:
...
from guppylang.std.quantum import qubit
# Need to make sure that the quantum module is loaded
qubit_defn = self.globals[qubit.id]
assert isinstance(qubit_defn, TypeDef)
qubit_ty = qubit_defn.check_instantiate([], self.globals)
# Check against qubit_ty
Also, this only works if the quantum
module is loaded. Therefore, it might be a good idea to move state_result
into quantum
? Or its own debug
module that depends on quantum
?
guppylang/compiler/expr_compiler.py
Outdated
qubits_arr = self.builder.add_op(array_new(ht.Qubit, num_qubits), *qubits_in) | ||
qubits_arr = self.builder.add_op(op, qubits_arr) | ||
qubits_out = unpack_array(self.builder, qubits_arr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if using arrays in the hugr op is worth this overhead or there is a better way of representing the list of qubits in the extension
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine for now. Would be a good idea to add an overload of result_state
that takes an array anyways as a follow-up
10 | def main() -> None: | ||
11 | q1 = qubit() | ||
12 | state_result(q1) | ||
| ^^ Expected expression of type `str`, got `qubit` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(While it would be nice to be more specific about the missing tag this is the same as result at the moment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine and also consistent with the message you would get when calling regular functions 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Just a couple of nits and suggestions
guppylang/std/_internal/checker.py
Outdated
raise GuppyTypeError(ExpectedError(tag, "a string literal")) | ||
if len(tag.value.encode("utf-8")) > TAG_MAX_LEN: | ||
err: Error = TooLongError(tag) | ||
err.add_sub_diagnostic(TooLongError.Hint(None)) | ||
raise GuppyTypeError(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add tests for those two errors?
10 | def main() -> None: | ||
11 | q1 = qubit() | ||
12 | state_result(q1) | ||
| ^^ Expected expression of type `str`, got `qubit` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine and also consistent with the message you would get when calling regular functions 👍
guppylang/compiler/expr_compiler.py
Outdated
) | ||
|
||
qubits_in = [self.visit(e) for e in node.args[1:]] | ||
# TODO: This should be caught during checking. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that qubit
is a library defined type and not specifically known by the compiler (unlike the types in builtins
).
You would need to do something like the following:
class StateResultChecker(CustomCallChecker):
"""Call checker for the `state_result` function."""
def synthesize(self, args: list[ast.expr]) -> tuple[ast.expr, Type]:
...
from guppylang.std.quantum import qubit
# Need to make sure that the quantum module is loaded
qubit_defn = self.globals[qubit.id]
assert isinstance(qubit_defn, TypeDef)
qubit_ty = qubit_defn.check_instantiate([], self.globals)
# Check against qubit_ty
Also, this only works if the quantum
module is loaded. Therefore, it might be a good idea to move state_result
into quantum
? Or its own debug
module that depends on quantum
?
guppylang/compiler/expr_compiler.py
Outdated
qubits_arr = self.builder.add_op(array_new(ht.Qubit, num_qubits), *qubits_in) | ||
qubits_arr = self.builder.add_op(op, qubits_arr) | ||
qubits_out = unpack_array(self.builder, qubits_arr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine for now. Would be a good idea to add an overload of result_state
that takes an array anyways as a follow-up
guppylang/std/_internal/checker.py
Outdated
err.add_sub_diagnostic(TooLongError.Hint(None)) | ||
raise GuppyTypeError(err) | ||
|
||
tys = [ExprSynthesizer(self.ctx).synthesize(val)[1] for val in args] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You shouldn't ignore the expression returned by synthesize
. E.g. instead
tys = [ExprSynthesizer(self.ctx).synthesize(val)[1] for val in args] | |
args, tys = zip(*(ExprSynthesizer(self.ctx).synthesize(val) for val in args), strict=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also no need to resynthesize the type for args[0]
since you already checked it above
guppylang/std/_internal/checker.py
Outdated
[FuncInput(tys[0], InputFlags.NoFlags)] | ||
+ [FuncInput(t, InputFlags.Inout) for t in tys[1:]], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you could just do
[FuncInput(tys[0], InputFlags.NoFlags)] | |
+ [FuncInput(t, InputFlags.Inout) for t in tys[1:]], | |
[FuncInput(string_type(), InputFlags.NoFlags)] | |
+ [FuncInput(t, InputFlags.Inout) for t in tys], |
here
9 | state_result("tag", x) | ||
| ^^^^^^^^^^^^^^^^^^^^^^ Non-qubit inputs to state_result are not supported |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nicer if the span only highlighted x
def test_empty(validate): | ||
@compile_quantum_guppy | ||
def main() -> None: | ||
state_result("tag") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if we should allow this. I could see users writing this with the assumption that the whole state will be outputted because they are not aware that you have to pass the qubits.
Since empty state results are useless anyways, maybe it would be more helpful to add a diagnostic along the lines of
2 | state_result("tag")
| ^ Qubits whose state should be reported must be passed explicitly
Closes #899