-
Notifications
You must be signed in to change notification settings - Fork 3
docs: Language guide for libraries #104
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
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
b48648f
use guppylang 0.21.13, remove tket version pin
CalMacCQ b961b42
bump guppylang submodule to 0.21.13 release tag
CalMacCQ 350f034
Start on the libraries guide
maximilianruesch 602938e
Finish up
maximilianruesch 9d9cacd
Document emulator
maximilianruesch 1c035c2
Merge branch 'main' into mr/docs/libraries-guide
maximilianruesch d77ecd7
Renew version
maximilianruesch 1b01f37
Fix hugr package ref
maximilianruesch ba948bd
Fix header casing
maximilianruesch ad708f8
Fix styling
maximilianruesch 897f59d
Fix wording
maximilianruesch e22dfb0
Extraneeous comma
maximilianruesch 29f70b7
Fix wording
maximilianruesch File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,4 +22,5 @@ static.md | |
| python_differences.md | ||
| ownership.md | ||
| comptime.md | ||
| libraries.md | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| --- | ||
| file_format: mystnb | ||
| kernelspec: | ||
| name: python3 | ||
| --- | ||
|
|
||
| # Libraries | ||
|
|
||
| Introduced in ``v0.21.13``. | ||
|
|
||
| ## Introduction | ||
|
|
||
| A Guppy *library* comprises a collection of functions that act as a single compilable unit. | ||
| As opposed to compiling a single function and its dependees, compiling a library may result in a compilation product (a HUGR package) with multiple functions acting as entrypoints. | ||
| These functions may take arguments, and have non-``None`` return types. | ||
| Libraries can be used for separating compilation of multiple parts of your codebase (thus allowing reuse of unchanged definitions), distributing packages that are highly optimized, and much more. | ||
|
|
||
| A library can be created with the ``guppy.library(...)`` function as follows: | ||
| ```{code-cell} ipython3 | ||
| from guppylang import guppy | ||
| from hugr.package import Package | ||
|
|
||
| @guppy | ||
| def my_func() -> None: | ||
| pass | ||
|
|
||
| @guppy | ||
| def another_func(x: int) -> None: | ||
| pass | ||
|
|
||
| @guppy | ||
| def a_third_func(x: int) -> int: | ||
| return x + 1 | ||
|
|
||
| # Creates the library object, but does not compile it | ||
| lib = guppy.library( | ||
| my_func, | ||
| another_func, | ||
| a_third_func, | ||
| ) | ||
| # Compiles the library to a HUGR package | ||
| lib_pkg: Package = lib.compile() | ||
| ``` | ||
| In the resulting ``lib_pkg``, all three functions are included with public visibility, as they act as the libraries entrypoints. | ||
| Any additional functions used by these entrypoints are also included in the package (similar to dependees of a single function compilation), but with private visibility. | ||
| The visibility of a function affects whether it is available for linking, explained [below](#linking-and-visibility). | ||
|
|
||
| ## Using libraries and stubs | ||
|
|
||
| To allow programming against the interface of the library, the library developer needs to expose a series of stubs, mirroring the entrypoint functions the library was created with. | ||
| In Guppy, a stub can be created as a function declaration with the ``@guppy.declare`` decorator. | ||
|
|
||
| For the introductory example, the stubs could look as follows: | ||
| ```{code-cell} ipython3 | ||
| from guppylang import guppy | ||
|
|
||
| @guppy.declare | ||
| def my_func() -> None: ... | ||
|
|
||
| @guppy.declare | ||
| def another_func(x: int) -> None: ... | ||
|
|
||
| @guppy.declare | ||
| def a_third_func(x: int) -> int: ... | ||
| ``` | ||
| Currently, creation of these stubs is a manual process; it is currently not possible to automatically generate stubs for a library or validate that definitions faithfully implement their corresponding stubs. | ||
|
|
||
| The end-user may then use these stubs as part of their regular Guppy source, and compile their code independently of the library: | ||
| ```{code-cell} ipython3 | ||
| from guppylang import guppy | ||
| from guppylang.std.builtins import result | ||
| from hugr.package import Package | ||
|
|
||
| # --- SNIP --- | ||
| # Taken from the declarations, usually you would import this symbol | ||
| @guppy.declare | ||
| def my_func() -> None: ... | ||
|
|
||
| @guppy.declare | ||
| def a_third_func(x: int) -> int: ... | ||
| # --- SNIP --- | ||
|
|
||
| @guppy | ||
| def consumer_func() -> None: | ||
| my_func() | ||
| result("library_call", a_third_func(5)) | ||
|
|
||
| @guppy | ||
| def main() -> None: | ||
| consumer_func() | ||
|
|
||
| user_pkg: Package = main.compile() | ||
| ``` | ||
| Here, the end-user aims to create an executable package: The compilation product is a HUGR package with a single parameter-free entrypoint function. | ||
| However, the ``user_pkg`` is incomplete, as it lacks the function definitions corresponding to the used function declarations. | ||
| This may cause issues when further processing of the package (at last when the contents of the package should be executed using e.g. a simulator), so the library package containing the definitions has to be *linked* in to provide them (see [below](#linking-and-visibility) for more information). | ||
|
|
||
| When building an emulator for an entrypoint function, the library packages to be linked in can be supplied using the `libs` keyword-argument: | ||
| ```{code-cell} ipython3 | ||
| --- | ||
| tags: [skip-execution] | ||
| --- | ||
| from guppylang import guppy | ||
|
CalMacCQ marked this conversation as resolved.
|
||
| from hugr.package import Package | ||
|
|
||
| # --- SNIP --- | ||
| lib_pkg: Package = ... | ||
|
|
||
| # Imported from somewhere | ||
| @guppy.declare | ||
| def my_func() -> None: ... | ||
| # --- SNIP --- | ||
|
|
||
| @guppy | ||
| def main() -> None: | ||
| my_func() | ||
|
|
||
| main.emulator(n_qubits=1, libs=[lib_pkg]).run() | ||
| ``` | ||
|
|
||
| ## Linking and visibility | ||
|
|
||
| As of the HUGR Python package `hugr>=0.16.0`, it is possible to *link* packages, replacing calls to function declarations with calls to the corresponding function definitions, if they are available: | ||
| ```{code-cell} ipython3 | ||
| --- | ||
| tags: [skip-execution] | ||
| --- | ||
| from hugr.package import Package | ||
|
|
||
| # --- SNIP --- | ||
| # Created as above | ||
| lib_pkg: Package = ... | ||
| user_pkg: Package = ... | ||
| # --- SNIP --- | ||
|
|
||
| combined_pkg: Package = user_pkg.link(user_pkg) | ||
| ``` | ||
| The order in which the packages are linked does not matter, and linking multiple packages can be done all at once, or in several calls. | ||
| It is also not required that, after linking, there are no remaining calls to (unrelated) function declarations. | ||
| If that is the case, the package may remain as unexecutable, and further linking has to be performed to fill those declarations. | ||
|
|
||
| Functions inside the package receive a visibility that is either public or private. | ||
| Only public functions are available for linking, where private functions are completely ignored. | ||
| While function declarations are always public, a function definition is public if and only if it was included as an entrypoint to the library. | ||
|
|
||
| Furthermore, functions inside the package will receive a *name*, with a default scheme based on the module that the Guppy function was defined in as well as the original name of the Guppy function. | ||
| For the introductory example, assuming the functions reside in a file at `src/mylib/foo/bar.py`, the names assigned to the functions in the package will be: | ||
| - ``mylib.foo.bar.my_func`` | ||
| - ``mylib.foo.bar.another_func`` | ||
| - ``mylib.foo.bar.a_third_func`` | ||
|
|
||
| To be able to link the corresponding declarations and the functions together, they need to have the same name inside the HUGR package. | ||
| In cases where the default name for declarations is wrong (e.g. when they are declared in some other module than the definitions or have different function names), the name they will receive can be manually overridden using the ``link_name`` keyword-argument: | ||
| ```{code-cell} ipython3 | ||
| from guppylang import guppy | ||
|
|
||
| @guppy(link_name="my.func.in.my.library") | ||
| def my_func() -> None: | ||
| pass | ||
|
|
||
| @guppy.declare(link_name="my.func.in.my.library") | ||
| def my_func_decl() -> None: ... | ||
| ``` | ||
| In this example, the linking process will be able to associate the declaration with the definition, even though their function names are different. | ||
|
|
||
| ## Structs and enums | ||
|
|
||
| It is also possible to make a ``@guppy.struct`` or a ``@guppy.enum`` part of the library interface. | ||
| In such cases, all methods of these types will be acting entrypoints of the package: | ||
| ```{code-cell} ipython3 | ||
| from guppylang import guppy | ||
|
|
||
| @guppy.struct | ||
| class MyStruct: | ||
| field_a: bool | ||
| field_b: int | ||
|
|
||
| @guppy | ||
| def my_method(self) -> None: # Will be an entrypoint of the package | ||
| pass | ||
|
|
||
| lib = guppy.library(MyStruct).compile() | ||
| ``` | ||
| A stub for such a type should replicate all fields, and contain stubs for the Guppy methods: | ||
| ```{code-cell} ipython3 | ||
| from guppylang import guppy | ||
|
|
||
| @guppy.struct | ||
| class MyStruct: | ||
| field_a: bool | ||
| field_b: int | ||
|
|
||
| @guppy.declare | ||
| def my_method(self) -> None: ... | ||
| ``` | ||
| Specifying ``link_name`` on these methods will work as with top-level functions, with the default value including the struct name as part of the method name. | ||
| However, the ``@guppy.struct`` and ``@guppy.enum`` decorators also support changing the name prefix up to and including the type name for all members using the default mechanism: | ||
| ```{code-cell} ipython3 | ||
| from guppylang import guppy | ||
|
|
||
| @guppy.struct(link_name="my.struct.path") | ||
| class MyStruct: | ||
| @guppy | ||
| def my_method(self) -> None: # will receive "my.struct.path.my_method" | ||
| pass | ||
|
|
||
| @guppy(link_name="override.name") | ||
| def my_other_method(self) -> None: # will receive "override.name" | ||
| pass | ||
|
|
||
| lib = guppy.library(MyStruct).compile() | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
What is the purpose of these
--- SNIP ---lines? Did you intend for them to show up in the html pages like this?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.
They are intended to show that the displayed code is not something that adds meaning for this section, but rather as a refresher from some other (earlier) code snippet on the page. Called
SNIPto signify how someone would could the code with a scissor to insert it here.