hugr 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hugr-0.1.0/PKG-INFO +79 -0
- hugr-0.1.0/README.md +51 -0
- hugr-0.1.0/pyproject.toml +32 -0
- hugr-0.1.0/src/hugr/__init__.py +8 -0
- hugr-0.1.0/src/hugr/py.typed +0 -0
- hugr-0.1.0/src/hugr/serialization/__init__.py +3 -0
- hugr-0.1.0/src/hugr/serialization/ops.py +524 -0
- hugr-0.1.0/src/hugr/serialization/serial_hugr.py +36 -0
- hugr-0.1.0/src/hugr/serialization/tys.py +319 -0
hugr-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: hugr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Quantinuum's common representation for quantum programs
|
|
5
|
+
Home-page: https://github.com/CQCL/hugr
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Author: TKET development team
|
|
8
|
+
Author-email: tket-support@cambridgequantum.com
|
|
9
|
+
Maintainer: TKET development team
|
|
10
|
+
Maintainer-email: tket-support@cambridgequantum.com
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Requires-Dist: pydantic (>=2.6.4,<3.0.0)
|
|
25
|
+
Project-URL: Repository, https://github.com/CQCL/hugr
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
hugr
|
|
29
|
+
===============
|
|
30
|
+
|
|
31
|
+
[![build_status][]](https://github.com/CQCL/hugr/actions)
|
|
32
|
+
[![codecov][]](https://codecov.io/gh/CQCL/hugr)
|
|
33
|
+
|
|
34
|
+
The Hierarchical Unified Graph Representation (HUGR, pronounced _hugger_) is the
|
|
35
|
+
common representation of quantum circuits and operations in the Quantinuum
|
|
36
|
+
ecosystem.
|
|
37
|
+
|
|
38
|
+
This library provides a pure-python implementation of the HUGR data model, and
|
|
39
|
+
a low-level API for constructing HUGR objects.
|
|
40
|
+
|
|
41
|
+
This library is intended to be used as a dependency for other high-level tools.
|
|
42
|
+
See [`guppylang`][] and [`tket2`][] for examples of such tools.
|
|
43
|
+
|
|
44
|
+
The HUGR specification is [here](https://github.com/CQCL/hugr/blob/main/specification/hugr.md).
|
|
45
|
+
|
|
46
|
+
[`guppylang`]: https://pypi.org/project/guppylang/
|
|
47
|
+
[`tket2`]: https://github.com/CQCL/tket2
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
The package name is `hugr`. It can be installed from PyPI:
|
|
53
|
+
```bash
|
|
54
|
+
pip install hugr
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The current releases are in alpha stage, and the API is subject to change.
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
TODO
|
|
62
|
+
|
|
63
|
+
## Recent Changes
|
|
64
|
+
|
|
65
|
+
TODO
|
|
66
|
+
|
|
67
|
+
## Development
|
|
68
|
+
|
|
69
|
+
TODO
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
This project is licensed under Apache License, Version 2.0 ([LICENSE][] or http://www.apache.org/licenses/LICENSE-2.0).
|
|
74
|
+
|
|
75
|
+
[build_status]: https://github.com/CQCL/hugr/actions/workflows/ci-py.yml/badge.svg?branch=main
|
|
76
|
+
[codecov]: https://img.shields.io/codecov/c/gh/CQCL/hugr?logo=codecov
|
|
77
|
+
[LICENSE]: https://github.com/CQCL/hugr/blob/main/LICENCE
|
|
78
|
+
[CHANGELOG]: https://github.com/CQCL/hugr/blob/main/hugr-py/CHANGELOG.md
|
|
79
|
+
|
hugr-0.1.0/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
hugr
|
|
2
|
+
===============
|
|
3
|
+
|
|
4
|
+
[![build_status][]](https://github.com/CQCL/hugr/actions)
|
|
5
|
+
[![codecov][]](https://codecov.io/gh/CQCL/hugr)
|
|
6
|
+
|
|
7
|
+
The Hierarchical Unified Graph Representation (HUGR, pronounced _hugger_) is the
|
|
8
|
+
common representation of quantum circuits and operations in the Quantinuum
|
|
9
|
+
ecosystem.
|
|
10
|
+
|
|
11
|
+
This library provides a pure-python implementation of the HUGR data model, and
|
|
12
|
+
a low-level API for constructing HUGR objects.
|
|
13
|
+
|
|
14
|
+
This library is intended to be used as a dependency for other high-level tools.
|
|
15
|
+
See [`guppylang`][] and [`tket2`][] for examples of such tools.
|
|
16
|
+
|
|
17
|
+
The HUGR specification is [here](https://github.com/CQCL/hugr/blob/main/specification/hugr.md).
|
|
18
|
+
|
|
19
|
+
[`guppylang`]: https://pypi.org/project/guppylang/
|
|
20
|
+
[`tket2`]: https://github.com/CQCL/tket2
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
The package name is `hugr`. It can be installed from PyPI:
|
|
26
|
+
```bash
|
|
27
|
+
pip install hugr
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The current releases are in alpha stage, and the API is subject to change.
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
TODO
|
|
35
|
+
|
|
36
|
+
## Recent Changes
|
|
37
|
+
|
|
38
|
+
TODO
|
|
39
|
+
|
|
40
|
+
## Development
|
|
41
|
+
|
|
42
|
+
TODO
|
|
43
|
+
|
|
44
|
+
## License
|
|
45
|
+
|
|
46
|
+
This project is licensed under Apache License, Version 2.0 ([LICENSE][] or http://www.apache.org/licenses/LICENSE-2.0).
|
|
47
|
+
|
|
48
|
+
[build_status]: https://github.com/CQCL/hugr/actions/workflows/ci-py.yml/badge.svg?branch=main
|
|
49
|
+
[codecov]: https://img.shields.io/codecov/c/gh/CQCL/hugr?logo=codecov
|
|
50
|
+
[LICENSE]: https://github.com/CQCL/hugr/blob/main/LICENCE
|
|
51
|
+
[CHANGELOG]: https://github.com/CQCL/hugr/blob/main/hugr-py/CHANGELOG.md
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
classifiers = [
|
|
3
|
+
"Environment :: Console",
|
|
4
|
+
"Programming Language :: Python :: 3.10",
|
|
5
|
+
"Programming Language :: Python :: 3.11",
|
|
6
|
+
"Programming Language :: Python :: 3.12",
|
|
7
|
+
"License :: OSI Approved :: Apache Software License",
|
|
8
|
+
"Operating System :: MacOS :: MacOS X",
|
|
9
|
+
"Operating System :: POSIX :: Linux",
|
|
10
|
+
"Operating System :: Microsoft :: Windows",
|
|
11
|
+
"Intended Audience :: Developers",
|
|
12
|
+
"Intended Audience :: Science/Research",
|
|
13
|
+
"Topic :: Scientific/Engineering",
|
|
14
|
+
]
|
|
15
|
+
name = "hugr"
|
|
16
|
+
version = "0.1.0"
|
|
17
|
+
description = "Quantinuum's common representation for quantum programs"
|
|
18
|
+
#keywords = []
|
|
19
|
+
authors = ["TKET development team <tket-support@cambridgequantum.com>"]
|
|
20
|
+
maintainers = ["TKET development team <tket-support@cambridgequantum.com>"]
|
|
21
|
+
license = "Apache-2.0"
|
|
22
|
+
readme = "README.md"
|
|
23
|
+
homepage = "https://github.com/CQCL/hugr"
|
|
24
|
+
repository = "https://github.com/CQCL/hugr"
|
|
25
|
+
|
|
26
|
+
[tool.poetry.dependencies]
|
|
27
|
+
python = ">=3.10"
|
|
28
|
+
pydantic = "^2.6.4"
|
|
29
|
+
|
|
30
|
+
[tool.pytest.ini_options]
|
|
31
|
+
# Lark throws deprecation warnings for `src_parse` and `src_constants`.
|
|
32
|
+
filterwarnings = "ignore::DeprecationWarning:lark.*"
|
|
File without changes
|
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import sys
|
|
3
|
+
from abc import ABC
|
|
4
|
+
from typing import Any, Literal, cast
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field, RootModel
|
|
7
|
+
|
|
8
|
+
from . import tys
|
|
9
|
+
from .tys import (
|
|
10
|
+
ExtensionId,
|
|
11
|
+
ExtensionSet,
|
|
12
|
+
FunctionType,
|
|
13
|
+
PolyFuncType,
|
|
14
|
+
Type,
|
|
15
|
+
TypeRow,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
NodeID = int
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseOp(ABC, BaseModel):
|
|
22
|
+
"""Base class for ops that store their node's input/output types"""
|
|
23
|
+
|
|
24
|
+
# Parent node index of node the op belongs to, used only at serialization time
|
|
25
|
+
parent: NodeID
|
|
26
|
+
input_extensions: ExtensionSet = Field(default_factory=ExtensionSet)
|
|
27
|
+
|
|
28
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
29
|
+
"""Hook to insert type information from the input and output ports into the
|
|
30
|
+
op"""
|
|
31
|
+
|
|
32
|
+
def insert_child_dfg_signature(self, inputs: TypeRow, outputs: TypeRow) -> None:
|
|
33
|
+
"""Hook to insert type information from a child dataflow graph"""
|
|
34
|
+
|
|
35
|
+
def display_name(self) -> str:
|
|
36
|
+
"""Name of the op for visualisation"""
|
|
37
|
+
return self.__class__.__name__
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ----------------------------------------------------------
|
|
41
|
+
# --------------- Module level operations ------------------
|
|
42
|
+
# ----------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Module(BaseOp):
|
|
46
|
+
"""The root of a module, parent of all other `ModuleOp`s."""
|
|
47
|
+
|
|
48
|
+
op: Literal["Module"] = "Module"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class FuncDefn(BaseOp):
|
|
52
|
+
"""A function definition. Children nodes are the body of the definition."""
|
|
53
|
+
|
|
54
|
+
op: Literal["FuncDefn"] = "FuncDefn"
|
|
55
|
+
|
|
56
|
+
name: str
|
|
57
|
+
signature: PolyFuncType = Field(default_factory=PolyFuncType.empty)
|
|
58
|
+
|
|
59
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
60
|
+
assert len(in_types) == 0
|
|
61
|
+
assert len(out_types) == 1
|
|
62
|
+
out = out_types[0]
|
|
63
|
+
assert isinstance(out, PolyFuncType)
|
|
64
|
+
self.signature = out # TODO: Extensions
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class FuncDecl(BaseOp):
|
|
68
|
+
"""External function declaration, linked at runtime."""
|
|
69
|
+
|
|
70
|
+
op: Literal["FuncDecl"] = "FuncDecl"
|
|
71
|
+
name: str
|
|
72
|
+
signature: PolyFuncType = Field(default_factory=PolyFuncType.empty)
|
|
73
|
+
|
|
74
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
75
|
+
assert len(in_types) == 0
|
|
76
|
+
assert len(out_types) == 1
|
|
77
|
+
out = out_types[0]
|
|
78
|
+
assert isinstance(out, PolyFuncType)
|
|
79
|
+
self.signature = out
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ConstBase(BaseOp):
|
|
83
|
+
"""A constant operation definition."""
|
|
84
|
+
|
|
85
|
+
op: Literal["Const"] = "Const"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
CustomConst = Any # TODO
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class ExtensionConst(ConstBase):
|
|
92
|
+
"""An extension constant value, that can check it is of a given [CustomType]."""
|
|
93
|
+
|
|
94
|
+
c: Literal["Extension"] = Field("Extension", title="ConstTag")
|
|
95
|
+
e: CustomConst = Field(title="CustomConst")
|
|
96
|
+
|
|
97
|
+
class Config:
|
|
98
|
+
json_schema_extra = {
|
|
99
|
+
"required": ["parent", "op", "c", "e"],
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class FunctionConst(ConstBase):
|
|
104
|
+
"""A higher-order function value."""
|
|
105
|
+
|
|
106
|
+
c: Literal["Function"] = Field("Function", title="ConstTag")
|
|
107
|
+
hugr: Any # TODO
|
|
108
|
+
|
|
109
|
+
class Config:
|
|
110
|
+
json_schema_extra = {
|
|
111
|
+
"required": ["parent", "op", "c", "hugr"],
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class Tuple(ConstBase):
|
|
116
|
+
"""A constant tuple value."""
|
|
117
|
+
|
|
118
|
+
c: Literal["Tuple"] = Field("Tuple", title="ConstTag")
|
|
119
|
+
vs: list["Const"]
|
|
120
|
+
|
|
121
|
+
class Config:
|
|
122
|
+
json_schema_extra = {
|
|
123
|
+
"required": ["parent", "op", "c", "vs"],
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class Sum(ConstBase):
|
|
128
|
+
"""A Sum variant
|
|
129
|
+
|
|
130
|
+
For any Sum type where this value meets the type of the variant indicated by the tag
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
c: Literal["Sum"] = Field("Sum", title="ConstTag")
|
|
134
|
+
tag: int
|
|
135
|
+
typ: Type
|
|
136
|
+
vs: list["Const"]
|
|
137
|
+
|
|
138
|
+
class Config:
|
|
139
|
+
# Needed to avoid random '\n's in the pydantic description
|
|
140
|
+
json_schema_extra = {
|
|
141
|
+
"description": (
|
|
142
|
+
"A Sum variant For any Sum type where this value meets the type "
|
|
143
|
+
"of the variant indicated by the tag."
|
|
144
|
+
),
|
|
145
|
+
"required": ["parent", "op", "c", "tag", "typ", "vs"],
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class Const(RootModel):
|
|
150
|
+
"""A constant operation."""
|
|
151
|
+
|
|
152
|
+
root: ExtensionConst | FunctionConst | Tuple | Sum = Field(discriminator="c")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# -----------------------------------------------
|
|
156
|
+
# --------------- BasicBlock types ------------------
|
|
157
|
+
# -----------------------------------------------
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class DataflowBlock(BaseOp):
|
|
161
|
+
"""A CFG basic block node. The signature is that of the internal Dataflow
|
|
162
|
+
graph."""
|
|
163
|
+
|
|
164
|
+
op: Literal["DataflowBlock"] = "DataflowBlock"
|
|
165
|
+
inputs: TypeRow = Field(default_factory=list)
|
|
166
|
+
other_outputs: TypeRow = Field(default_factory=list)
|
|
167
|
+
sum_rows: list[TypeRow] = Field(default_factory=list)
|
|
168
|
+
extension_delta: ExtensionSet = Field(default_factory=list)
|
|
169
|
+
|
|
170
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
171
|
+
num_cases = len(out_types)
|
|
172
|
+
self.sum_rows = [[] for _ in range(num_cases)]
|
|
173
|
+
|
|
174
|
+
def insert_child_dfg_signature(self, inputs: TypeRow, outputs: TypeRow) -> None:
|
|
175
|
+
self.inputs = inputs
|
|
176
|
+
pred = outputs[0]
|
|
177
|
+
assert isinstance(pred, tys.UnitSum | tys.GeneralSum)
|
|
178
|
+
if isinstance(pred, tys.UnitSum):
|
|
179
|
+
self.sum_rows = [[] for _ in range(cast(tys.UnitSum, pred).size)]
|
|
180
|
+
else:
|
|
181
|
+
self.sum_rows = []
|
|
182
|
+
for variant in pred.rows:
|
|
183
|
+
self.sum_rows.append(variant)
|
|
184
|
+
self.other_outputs = outputs[1:]
|
|
185
|
+
|
|
186
|
+
class Config:
|
|
187
|
+
# Needed to avoid random '\n's in the pydantic description
|
|
188
|
+
json_schema_extra = {
|
|
189
|
+
"required": [
|
|
190
|
+
"parent",
|
|
191
|
+
"op",
|
|
192
|
+
"inputs",
|
|
193
|
+
"other_outputs",
|
|
194
|
+
"sum_rows",
|
|
195
|
+
"extension_delta",
|
|
196
|
+
],
|
|
197
|
+
"description": "A CFG basic block node. The signature is that of the internal Dataflow graph.",
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class ExitBlock(BaseOp):
|
|
202
|
+
"""The single exit node of the CFG, has no children, stores the types of
|
|
203
|
+
the CFG node output."""
|
|
204
|
+
|
|
205
|
+
op: Literal["ExitBlock"] = "ExitBlock"
|
|
206
|
+
cfg_outputs: TypeRow
|
|
207
|
+
|
|
208
|
+
class Config:
|
|
209
|
+
# Needed to avoid random '\n's in the pydantic description
|
|
210
|
+
json_schema_extra = {
|
|
211
|
+
"description": "The single exit node of the CFG, has no children, stores the types of the CFG node output."
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ---------------------------------------------
|
|
216
|
+
# --------------- DataflowOp ------------------
|
|
217
|
+
# ---------------------------------------------
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class DataflowOp(BaseOp):
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class Input(DataflowOp):
|
|
225
|
+
"""An input node. The outputs of this node are the inputs to the parent node."""
|
|
226
|
+
|
|
227
|
+
op: Literal["Input"] = "Input"
|
|
228
|
+
types: TypeRow = Field(default_factory=list)
|
|
229
|
+
|
|
230
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
231
|
+
assert len(in_types) == 0
|
|
232
|
+
self.types = list(out_types)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class Output(DataflowOp):
|
|
236
|
+
"""An output node. The inputs are the outputs of the function."""
|
|
237
|
+
|
|
238
|
+
op: Literal["Output"] = "Output"
|
|
239
|
+
types: TypeRow = Field(default_factory=list)
|
|
240
|
+
|
|
241
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
242
|
+
assert len(out_types) == 0
|
|
243
|
+
self.types = list(in_types)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class Call(DataflowOp):
|
|
247
|
+
"""
|
|
248
|
+
Call a function directly.
|
|
249
|
+
|
|
250
|
+
The first port is connected to the def/declare of the function being called
|
|
251
|
+
directly, with a `ConstE<Graph>` edge. The signature of the remaining ports matches
|
|
252
|
+
the function being called.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
op: Literal["Call"] = "Call"
|
|
256
|
+
signature: FunctionType = Field(default_factory=FunctionType.empty)
|
|
257
|
+
|
|
258
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
259
|
+
# The constE edge comes after the value inputs
|
|
260
|
+
fun_ty = in_types[-1]
|
|
261
|
+
assert isinstance(fun_ty, PolyFuncType)
|
|
262
|
+
poly_func = cast(PolyFuncType, fun_ty)
|
|
263
|
+
assert len(poly_func.params) == 0
|
|
264
|
+
self.signature = poly_func.body
|
|
265
|
+
|
|
266
|
+
class Config:
|
|
267
|
+
# Needed to avoid random '\n's in the pydantic description
|
|
268
|
+
json_schema_extra = {
|
|
269
|
+
"description": (
|
|
270
|
+
"Operation to call a function directly. The first port is "
|
|
271
|
+
"connected to the def/declare of the function being called directly, "
|
|
272
|
+
"with a `Static<FunctionType>` edge. The signature of the remaining "
|
|
273
|
+
"ports matches the function being called."
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class CallIndirect(DataflowOp):
|
|
279
|
+
"""Call a function indirectly.
|
|
280
|
+
|
|
281
|
+
Like call, but the first input is a standard dataflow graph type."""
|
|
282
|
+
|
|
283
|
+
op: Literal["CallIndirect"] = "CallIndirect"
|
|
284
|
+
signature: FunctionType = Field(default_factory=FunctionType.empty)
|
|
285
|
+
|
|
286
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
287
|
+
fun_ty = in_types[0]
|
|
288
|
+
assert isinstance(fun_ty, PolyFuncType)
|
|
289
|
+
poly_func = cast(PolyFuncType, fun_ty)
|
|
290
|
+
assert len(poly_func.params) == 0
|
|
291
|
+
assert len(poly_func.body.input) == len(in_types) - 1
|
|
292
|
+
assert len(poly_func.body.output) == len(out_types)
|
|
293
|
+
self.signature = poly_func.body
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class LoadConstant(DataflowOp):
|
|
297
|
+
"""An operation that loads a static constant in to the local dataflow graph."""
|
|
298
|
+
|
|
299
|
+
op: Literal["LoadConstant"] = "LoadConstant"
|
|
300
|
+
datatype: Type
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class DFG(DataflowOp):
|
|
304
|
+
"""A simply nested dataflow graph."""
|
|
305
|
+
|
|
306
|
+
op: Literal["DFG"] = "DFG"
|
|
307
|
+
signature: FunctionType = Field(default_factory=FunctionType.empty)
|
|
308
|
+
|
|
309
|
+
def insert_child_dfg_signature(self, inputs: TypeRow, outputs: TypeRow) -> None:
|
|
310
|
+
self.signature = FunctionType(
|
|
311
|
+
input=list(inputs), output=list(outputs), extension_reqs=ExtensionSet([])
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
# ------------------------------------------------
|
|
316
|
+
# --------------- ControlFlowOp ------------------
|
|
317
|
+
# ------------------------------------------------
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class Conditional(DataflowOp):
|
|
321
|
+
"""Conditional operation, defined by child `Case` nodes for each branch."""
|
|
322
|
+
|
|
323
|
+
op: Literal["Conditional"] = "Conditional"
|
|
324
|
+
other_inputs: TypeRow = Field(default_factory=list) # Remaining input types
|
|
325
|
+
outputs: TypeRow = Field(default_factory=list) # Output types
|
|
326
|
+
sum_rows: list[TypeRow] = Field(description="The possible rows of the Sum input")
|
|
327
|
+
# Extensions used to produce the outputs
|
|
328
|
+
extension_delta: ExtensionSet = Field(default_factory=list)
|
|
329
|
+
|
|
330
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
331
|
+
# First port is a predicate, i.e. a sum of tuple types. We need to unpack
|
|
332
|
+
# those into a list of type rows
|
|
333
|
+
pred = in_types[0]
|
|
334
|
+
if isinstance(pred, tys.UnitSum):
|
|
335
|
+
self.sum_rows = [[] for _ in range(cast(tys.UnitSum, pred).size)]
|
|
336
|
+
else:
|
|
337
|
+
assert isinstance(pred, tys.GeneralSum)
|
|
338
|
+
self.sum_rows = []
|
|
339
|
+
for ty in pred.rows:
|
|
340
|
+
self.sum_rows.append(ty)
|
|
341
|
+
self.other_inputs = list(in_types[1:])
|
|
342
|
+
self.outputs = list(out_types)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class Case(BaseOp):
|
|
346
|
+
"""Case ops - nodes valid inside Conditional nodes."""
|
|
347
|
+
|
|
348
|
+
op: Literal["Case"] = "Case"
|
|
349
|
+
# The signature of the contained dataflow graph.
|
|
350
|
+
signature: FunctionType = Field(default_factory=FunctionType.empty)
|
|
351
|
+
|
|
352
|
+
def insert_child_dfg_signature(self, inputs: TypeRow, outputs: TypeRow) -> None:
|
|
353
|
+
self.signature = tys.FunctionType(
|
|
354
|
+
input=list(inputs), output=list(outputs), extension_reqs=ExtensionSet([])
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class TailLoop(DataflowOp):
|
|
359
|
+
"""Tail-controlled loop."""
|
|
360
|
+
|
|
361
|
+
op: Literal["TailLoop"] = "TailLoop"
|
|
362
|
+
just_inputs: TypeRow = Field(default_factory=list) # Types that are only input
|
|
363
|
+
just_outputs: TypeRow = Field(default_factory=list) # Types that are only output
|
|
364
|
+
# Types that are appended to both input and output:
|
|
365
|
+
rest: TypeRow = Field(default_factory=list)
|
|
366
|
+
|
|
367
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
368
|
+
assert in_types == out_types
|
|
369
|
+
# self.just_inputs = list(in_types)
|
|
370
|
+
# self.just_outputs = list(out_types)
|
|
371
|
+
self.rest = list(in_types)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class CFG(DataflowOp):
|
|
375
|
+
"""A dataflow node which is defined by a child CFG."""
|
|
376
|
+
|
|
377
|
+
op: Literal["CFG"] = "CFG"
|
|
378
|
+
signature: FunctionType = Field(default_factory=FunctionType.empty)
|
|
379
|
+
|
|
380
|
+
def insert_port_types(self, inputs: TypeRow, outputs: TypeRow) -> None:
|
|
381
|
+
self.signature = FunctionType(
|
|
382
|
+
input=list(inputs), output=list(outputs), extension_reqs=ExtensionSet([])
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
ControlFlowOp = Conditional | TailLoop | CFG
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class CustomOp(DataflowOp):
|
|
390
|
+
"""A user-defined operation that can be downcasted by the extensions that define
|
|
391
|
+
it."""
|
|
392
|
+
|
|
393
|
+
op: Literal["CustomOp"] = "CustomOp"
|
|
394
|
+
extension: ExtensionId
|
|
395
|
+
op_name: str
|
|
396
|
+
signature: tys.FunctionType = Field(default_factory=tys.FunctionType.empty)
|
|
397
|
+
description: str = ""
|
|
398
|
+
args: list[tys.TypeArg] = Field(default_factory=list)
|
|
399
|
+
|
|
400
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
401
|
+
self.signature = tys.FunctionType(input=list(in_types), output=list(out_types))
|
|
402
|
+
|
|
403
|
+
def display_name(self) -> str:
|
|
404
|
+
return self.op_name
|
|
405
|
+
|
|
406
|
+
class Config:
|
|
407
|
+
# Needed to avoid random '\n's in the pydantic description
|
|
408
|
+
json_schema_extra = {
|
|
409
|
+
"description": (
|
|
410
|
+
"A user-defined operation that can be downcasted by the extensions that "
|
|
411
|
+
"define it."
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class Noop(DataflowOp):
|
|
417
|
+
"""A no-op operation."""
|
|
418
|
+
|
|
419
|
+
op: Literal["Noop"] = "Noop"
|
|
420
|
+
ty: Type
|
|
421
|
+
|
|
422
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
423
|
+
assert len(in_types) == 1
|
|
424
|
+
assert len(out_types) == 1
|
|
425
|
+
assert in_types[0] == out_types[0]
|
|
426
|
+
self.ty = in_types[0]
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
class MakeTuple(DataflowOp):
|
|
430
|
+
"""An operation that packs all its inputs into a tuple."""
|
|
431
|
+
|
|
432
|
+
op: Literal["MakeTuple"] = "MakeTuple"
|
|
433
|
+
tys: TypeRow = Field(default_factory=list)
|
|
434
|
+
|
|
435
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
436
|
+
# If we have a single order edge as input, this is a unit
|
|
437
|
+
if in_types == [None]:
|
|
438
|
+
in_types = []
|
|
439
|
+
self.tys = list(in_types)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
class UnpackTuple(DataflowOp):
|
|
443
|
+
"""An operation that packs all its inputs into a tuple."""
|
|
444
|
+
|
|
445
|
+
op: Literal["UnpackTuple"] = "UnpackTuple"
|
|
446
|
+
tys: TypeRow = Field(default_factory=list)
|
|
447
|
+
|
|
448
|
+
def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None:
|
|
449
|
+
self.tys = list(out_types)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
class Tag(DataflowOp):
|
|
453
|
+
"""An operation that creates a tagged sum value from one of its variants."""
|
|
454
|
+
|
|
455
|
+
op: Literal["Tag"] = "Tag"
|
|
456
|
+
tag: int # The variant to create.
|
|
457
|
+
variants: TypeRow # The variants of the sum type.
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
class Lift(DataflowOp):
|
|
461
|
+
"""Fixes some TypeParams of a polymorphic type by providing TypeArgs."""
|
|
462
|
+
|
|
463
|
+
op: Literal["Lift"] = "Lift"
|
|
464
|
+
type_row: TypeRow
|
|
465
|
+
new_extension: ExtensionId
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
class OpType(RootModel):
|
|
469
|
+
"""A constant operation."""
|
|
470
|
+
|
|
471
|
+
root: (
|
|
472
|
+
Module
|
|
473
|
+
| Case
|
|
474
|
+
| FuncDefn
|
|
475
|
+
| FuncDecl
|
|
476
|
+
| Const
|
|
477
|
+
| DataflowBlock
|
|
478
|
+
| ExitBlock
|
|
479
|
+
| Conditional
|
|
480
|
+
| TailLoop
|
|
481
|
+
| CFG
|
|
482
|
+
| Input
|
|
483
|
+
| Output
|
|
484
|
+
| Call
|
|
485
|
+
| CallIndirect
|
|
486
|
+
| LoadConstant
|
|
487
|
+
| CustomOp
|
|
488
|
+
| Noop
|
|
489
|
+
| MakeTuple
|
|
490
|
+
| UnpackTuple
|
|
491
|
+
| Tag
|
|
492
|
+
| Lift
|
|
493
|
+
| DFG
|
|
494
|
+
) = Field(discriminator="op")
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
# --------------------------------------
|
|
498
|
+
# --------------- OpDef ----------------
|
|
499
|
+
# --------------------------------------
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
class OpDef(BaseOp, populate_by_name=True):
|
|
503
|
+
"""Serializable definition for dynamically loaded operations."""
|
|
504
|
+
|
|
505
|
+
name: str # Unique identifier of the operation.
|
|
506
|
+
description: str # Human readable description of the operation.
|
|
507
|
+
inputs: list[tuple[str | None, Type]]
|
|
508
|
+
outputs: list[tuple[str | None, Type]]
|
|
509
|
+
misc: dict[str, Any] # Miscellaneous data associated with the operation.
|
|
510
|
+
def_: str | None = Field(
|
|
511
|
+
..., alias="def"
|
|
512
|
+
) # (YAML?)-encoded definition of the operation.
|
|
513
|
+
extension_reqs: ExtensionSet # Resources required to execute this operation.
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
# Now that all classes are defined, we need to update the ForwardRefs in all type
|
|
517
|
+
# annotations. We use some inspect magic to find all classes defined in this file.
|
|
518
|
+
classes = inspect.getmembers(
|
|
519
|
+
sys.modules[__name__],
|
|
520
|
+
lambda member: inspect.isclass(member) and member.__module__ == __name__,
|
|
521
|
+
)
|
|
522
|
+
for _, c in classes:
|
|
523
|
+
if issubclass(c, BaseModel):
|
|
524
|
+
c.model_rebuild()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from typing import Any, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from .ops import NodeID, OpType
|
|
6
|
+
|
|
7
|
+
Port = tuple[NodeID, int | None] # (node, offset)
|
|
8
|
+
Edge = tuple[Port, Port]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SerialHugr(BaseModel):
|
|
12
|
+
"""A serializable representation of a Hugr."""
|
|
13
|
+
|
|
14
|
+
version: Literal["v1"] = "v1"
|
|
15
|
+
nodes: list[OpType]
|
|
16
|
+
edges: list[Edge]
|
|
17
|
+
|
|
18
|
+
def to_json(self) -> str:
|
|
19
|
+
"""Return a JSON representation of the Hugr."""
|
|
20
|
+
return self.model_dump_json()
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def load_json(cls, json: dict[Any, Any]) -> "SerialHugr":
|
|
24
|
+
"""Decode a JSON-encoded Hugr."""
|
|
25
|
+
return cls(**json)
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def get_version(cls) -> str:
|
|
29
|
+
"""Return the version of the schema."""
|
|
30
|
+
return cls(nodes=[], edges=[]).version
|
|
31
|
+
|
|
32
|
+
class Config:
|
|
33
|
+
title = "Hugr"
|
|
34
|
+
json_schema_extra = {
|
|
35
|
+
"required": ["version", "nodes", "edges"],
|
|
36
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import sys
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Annotated, Any, Literal, Optional, Union
|
|
5
|
+
|
|
6
|
+
from pydantic import (
|
|
7
|
+
BaseModel,
|
|
8
|
+
Field,
|
|
9
|
+
RootModel,
|
|
10
|
+
ValidationError,
|
|
11
|
+
ValidationInfo,
|
|
12
|
+
ValidatorFunctionWrapHandler,
|
|
13
|
+
WrapValidator,
|
|
14
|
+
)
|
|
15
|
+
from pydantic_core import PydanticCustomError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _json_custom_error_validator(
|
|
19
|
+
value: Any, handler: ValidatorFunctionWrapHandler, _info: ValidationInfo
|
|
20
|
+
) -> Any:
|
|
21
|
+
"""Simplify the error message to avoid a gross error stemming
|
|
22
|
+
from exhaustive checking of all union options.
|
|
23
|
+
|
|
24
|
+
As suggested at
|
|
25
|
+
https://docs.pydantic.dev/latest/concepts/types/#named-recursive-types
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
Used to define named recursive alias types.
|
|
29
|
+
"""
|
|
30
|
+
try:
|
|
31
|
+
return handler(value)
|
|
32
|
+
except ValidationError as err:
|
|
33
|
+
raise PydanticCustomError(
|
|
34
|
+
"invalid_json",
|
|
35
|
+
"Input is not valid json",
|
|
36
|
+
) from err
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
ExtensionId = str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ExtensionSet(RootModel):
|
|
43
|
+
"""A set of extensions ids."""
|
|
44
|
+
|
|
45
|
+
root: Optional[list[ExtensionId]] = Field(default=None)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# --------------------------------------------
|
|
49
|
+
# --------------- TypeParam ------------------
|
|
50
|
+
# --------------------------------------------
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TypeTypeParam(BaseModel):
|
|
54
|
+
tp: Literal["Type"] = "Type"
|
|
55
|
+
b: "TypeBound"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class BoundedNatParam(BaseModel):
|
|
59
|
+
tp: Literal["BoundedNat"] = "BoundedNat"
|
|
60
|
+
bound: int | None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class OpaqueParam(BaseModel):
|
|
64
|
+
tp: Literal["Opaque"] = "Opaque"
|
|
65
|
+
ty: "Opaque"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ListParam(BaseModel):
|
|
69
|
+
tp: Literal["List"] = "List"
|
|
70
|
+
param: "TypeParam"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TupleParam(BaseModel):
|
|
74
|
+
tp: Literal["Tuple"] = "Tuple"
|
|
75
|
+
params: list["TypeParam"]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class TypeParam(RootModel):
|
|
79
|
+
"""A type parameter."""
|
|
80
|
+
|
|
81
|
+
root: Annotated[
|
|
82
|
+
TypeTypeParam | BoundedNatParam | OpaqueParam | ListParam | TupleParam,
|
|
83
|
+
WrapValidator(_json_custom_error_validator),
|
|
84
|
+
] = Field(discriminator="tp")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ------------------------------------------
|
|
88
|
+
# --------------- TypeArg ------------------
|
|
89
|
+
# ------------------------------------------
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class CustomTypeArg(BaseModel):
|
|
93
|
+
typ: None # TODO
|
|
94
|
+
value: str
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class TypeTypeArg(BaseModel):
|
|
98
|
+
tya: Literal["Type"] = "Type"
|
|
99
|
+
ty: "Type"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class BoundedNatArg(BaseModel):
|
|
103
|
+
tya: Literal["BoundedNat"] = "BoundedNat"
|
|
104
|
+
n: int
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class OpaqueArg(BaseModel):
|
|
108
|
+
tya: Literal["Opaque"] = "Opaque"
|
|
109
|
+
arg: CustomTypeArg
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class SequenceArg(BaseModel):
|
|
113
|
+
tya: Literal["Sequence"] = "Sequence"
|
|
114
|
+
args: list["TypeArg"]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class ExtensionsArg(BaseModel):
|
|
118
|
+
tya: Literal["Extensions"] = "Extensions"
|
|
119
|
+
es: ExtensionSet
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class TypeArg(RootModel):
|
|
123
|
+
"""A type argument."""
|
|
124
|
+
|
|
125
|
+
root: Annotated[
|
|
126
|
+
TypeTypeArg | BoundedNatArg | OpaqueArg | SequenceArg | ExtensionsArg,
|
|
127
|
+
WrapValidator(_json_custom_error_validator),
|
|
128
|
+
] = Field(discriminator="tya")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# --------------------------------------------
|
|
132
|
+
# --------------- Container ------------------
|
|
133
|
+
# --------------------------------------------
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class MultiContainer(BaseModel):
|
|
137
|
+
ty: "Type"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class Array(MultiContainer):
|
|
141
|
+
"""Known size array whose elements are of the same type."""
|
|
142
|
+
|
|
143
|
+
t: Literal["Array"] = "Array"
|
|
144
|
+
len: int
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class UnitSum(BaseModel):
|
|
148
|
+
"""Simple predicate where all variants are empty tuples."""
|
|
149
|
+
|
|
150
|
+
t: Literal["Sum"] = "Sum"
|
|
151
|
+
|
|
152
|
+
s: Literal["Unit"] = "Unit"
|
|
153
|
+
size: int
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class GeneralSum(BaseModel):
|
|
157
|
+
"""General sum type that explicitly stores the types of the variants."""
|
|
158
|
+
|
|
159
|
+
t: Literal["Sum"] = "Sum"
|
|
160
|
+
|
|
161
|
+
s: Literal["General"] = "General"
|
|
162
|
+
rows: list["TypeRow"]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class SumType(RootModel):
|
|
166
|
+
root: Union[UnitSum, GeneralSum] = Field(discriminator="s")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# ----------------------------------------------
|
|
170
|
+
# --------------- ClassicType ------------------
|
|
171
|
+
# ----------------------------------------------
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class Variable(BaseModel):
|
|
175
|
+
"""A type variable identified by an index into the array of TypeParams."""
|
|
176
|
+
|
|
177
|
+
t: Literal["V"] = "V"
|
|
178
|
+
i: int
|
|
179
|
+
b: "TypeBound"
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class USize(BaseModel):
|
|
183
|
+
"""Unsigned integer size type."""
|
|
184
|
+
|
|
185
|
+
t: Literal["I"] = "I"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class FunctionType(BaseModel):
|
|
189
|
+
"""A graph encoded as a value. It contains a concrete signature and a set of
|
|
190
|
+
required resources."""
|
|
191
|
+
|
|
192
|
+
t: Literal["G"] = "G"
|
|
193
|
+
|
|
194
|
+
input: "TypeRow" # Value inputs of the function.
|
|
195
|
+
output: "TypeRow" # Value outputs of the function.
|
|
196
|
+
# The extension requirements which are added by the operation
|
|
197
|
+
extension_reqs: "ExtensionSet" = Field(default_factory=list)
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def empty(cls) -> "FunctionType":
|
|
201
|
+
return FunctionType(input=[], output=[], extension_reqs=ExtensionSet([]))
|
|
202
|
+
|
|
203
|
+
class Config:
|
|
204
|
+
# Needed to avoid random '\n's in the pydantic description
|
|
205
|
+
json_schema_extra = {
|
|
206
|
+
"description": (
|
|
207
|
+
"A graph encoded as a value. It contains a concrete signature and "
|
|
208
|
+
"a set of required resources."
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class PolyFuncType(BaseModel):
|
|
214
|
+
"""A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef.
|
|
215
|
+
(Nodes/operations in the Hugr are not polymorphic.)"""
|
|
216
|
+
|
|
217
|
+
# The declared type parameters, i.e., these must be instantiated with the same
|
|
218
|
+
# number of TypeArgs before the function can be called. This defines the indices
|
|
219
|
+
# used for variables within the body.
|
|
220
|
+
params: list[TypeParam]
|
|
221
|
+
|
|
222
|
+
# Template for the function. May contain variables up to length of `params`
|
|
223
|
+
body: FunctionType
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def empty(cls) -> "PolyFuncType":
|
|
227
|
+
return PolyFuncType(params=[], body=FunctionType.empty())
|
|
228
|
+
|
|
229
|
+
class Config:
|
|
230
|
+
# Needed to avoid random '\n's in the pydantic description
|
|
231
|
+
json_schema_extra = {
|
|
232
|
+
"description": (
|
|
233
|
+
"A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef. "
|
|
234
|
+
"(Nodes/operations in the Hugr are not polymorphic.)"
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class TypeBound(Enum):
|
|
240
|
+
Eq = "E"
|
|
241
|
+
Copyable = "C"
|
|
242
|
+
Any = "A"
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def join(*bs: "TypeBound") -> "TypeBound":
|
|
246
|
+
"""Computes the least upper bound for a sequence of bounds."""
|
|
247
|
+
res = TypeBound.Eq
|
|
248
|
+
for b in bs:
|
|
249
|
+
if b == TypeBound.Any:
|
|
250
|
+
return TypeBound.Any
|
|
251
|
+
if res == TypeBound.Eq:
|
|
252
|
+
res = b
|
|
253
|
+
return res
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class Opaque(BaseModel):
|
|
257
|
+
"""An opaque operation that can be downcasted by the extensions that define it."""
|
|
258
|
+
|
|
259
|
+
t: Literal["Opaque"] = "Opaque"
|
|
260
|
+
extension: ExtensionId
|
|
261
|
+
id: str # Unique identifier of the opaque type.
|
|
262
|
+
args: list[TypeArg]
|
|
263
|
+
bound: TypeBound
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# ----------------------------------------------
|
|
267
|
+
# --------------- LinearType -------------------
|
|
268
|
+
# ----------------------------------------------
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class Qubit(BaseModel):
|
|
272
|
+
"""A qubit."""
|
|
273
|
+
|
|
274
|
+
t: Literal["Q"] = "Q"
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class Type(RootModel):
|
|
278
|
+
"""A HUGR type."""
|
|
279
|
+
|
|
280
|
+
root: Annotated[
|
|
281
|
+
Qubit | Variable | USize | FunctionType | Array | SumType | Opaque,
|
|
282
|
+
WrapValidator(_json_custom_error_validator),
|
|
283
|
+
] = Field(discriminator="t")
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# -------------------------------------------
|
|
287
|
+
# --------------- TypeRow -------------------
|
|
288
|
+
# -------------------------------------------
|
|
289
|
+
|
|
290
|
+
TypeRow = list[Type]
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# -------------------------------------------
|
|
294
|
+
# --------------- Signature -----------------
|
|
295
|
+
# -------------------------------------------
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class Signature(BaseModel):
|
|
299
|
+
"""Describes the edges required to/from a node.
|
|
300
|
+
|
|
301
|
+
This includes both the concept of "signature" in the spec, and also the target
|
|
302
|
+
(value) of a call (constant).
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
signature: "PolyFuncType" # The underlying signature
|
|
306
|
+
|
|
307
|
+
# The extensions which are associated with all the inputs and carried through
|
|
308
|
+
input_extensions: ExtensionSet
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# Now that all classes are defined, we need to update the ForwardRefs in all type
|
|
312
|
+
# annotations. We use some inspect magic to find all classes defined in this file.
|
|
313
|
+
classes = inspect.getmembers(
|
|
314
|
+
sys.modules[__name__],
|
|
315
|
+
lambda member: inspect.isclass(member) and member.__module__ == __name__,
|
|
316
|
+
)
|
|
317
|
+
for _, c in classes:
|
|
318
|
+
if issubclass(c, BaseModel):
|
|
319
|
+
c.model_rebuild()
|