fpy2 0.0.2__tar.gz → 0.0.4__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.
Potentially problematic release.
This version of fpy2 might be problematic. Click here for more details.
- fpy2-0.0.4/PKG-INFO +113 -0
- fpy2-0.0.4/README.md +88 -0
- fpy2-0.0.4/fpy2/__init__.py +113 -0
- fpy2-0.0.4/fpy2/analysis/__init__.py +5 -0
- fpy2-0.0.4/fpy2/analysis/define_use.py +227 -0
- fpy2-0.0.4/fpy2/analysis/live_vars.py +213 -0
- fpy2-0.0.4/fpy2/analysis/syntax_check.py +399 -0
- fpy2-0.0.4/fpy2/ast/__init__.py +9 -0
- {fpy2-0.0.2/fpy2/frontend → fpy2-0.0.4/fpy2/ast}/formatter.py +147 -59
- fpy2-0.0.4/fpy2/ast/fpyast.py +1247 -0
- fpy2-0.0.4/fpy2/ast/visitor.py +590 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/backend/__init__.py +0 -1
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/backend/backend.py +2 -3
- fpy2-0.0.4/fpy2/backend/fpc.py +795 -0
- fpy2-0.0.4/fpy2/decorator.py +197 -0
- fpy2-0.0.4/fpy2/env.py +35 -0
- fpy2-0.0.4/fpy2/fpc_context.py +177 -0
- fpy2-0.0.4/fpy2/frontend/__init__.py +9 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/frontend/fpc.py +207 -176
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/frontend/parser.py +201 -147
- fpy2-0.0.4/fpy2/function.py +110 -0
- fpy2-0.0.4/fpy2/interpret/__init__.py +8 -0
- fpy2-0.0.4/fpy2/interpret/default.py +737 -0
- fpy2-0.0.4/fpy2/interpret/interpreter.py +83 -0
- fpy2-0.0.4/fpy2/interpret/native.py +673 -0
- fpy2-0.0.4/fpy2/libraries/__init__.py +0 -0
- fpy2-0.0.4/fpy2/libraries/core.py +158 -0
- fpy2-0.0.4/fpy2/math.py +520 -0
- fpy2-0.0.4/fpy2/number/__init__.py +295 -0
- fpy2-0.0.4/fpy2/number/context.py +219 -0
- fpy2-0.0.4/fpy2/number/ext_float.py +613 -0
- fpy2-0.0.4/fpy2/number/fixed.py +158 -0
- fpy2-0.0.4/fpy2/number/globals.py +44 -0
- fpy2-0.0.4/fpy2/number/gmp.py +526 -0
- fpy2-0.0.4/fpy2/number/ieee754.py +29 -0
- fpy2-0.0.4/fpy2/number/mp_fixed.py +305 -0
- fpy2-0.0.4/fpy2/number/mp_float.py +154 -0
- fpy2-0.0.4/fpy2/number/mpb_fixed.py +390 -0
- fpy2-0.0.4/fpy2/number/mpb_float.py +378 -0
- fpy2-0.0.4/fpy2/number/mps_float.py +299 -0
- fpy2-0.0.4/fpy2/number/native.py +40 -0
- fpy2-0.0.4/fpy2/number/number.py +1473 -0
- fpy2-0.0.4/fpy2/number/real.py +206 -0
- fpy2-0.0.4/fpy2/number/round.py +65 -0
- fpy2-0.0.4/fpy2/primitive.py +33 -0
- fpy2-0.0.4/fpy2/rewrite/__init__.py +9 -0
- fpy2-0.0.4/fpy2/rewrite/applier.py +237 -0
- fpy2-0.0.4/fpy2/rewrite/matcher.py +458 -0
- fpy2-0.0.4/fpy2/rewrite/pattern.py +100 -0
- fpy2-0.0.4/fpy2/rewrite/rewrite.py +233 -0
- fpy2-0.0.4/fpy2/rewrite/subst.py +61 -0
- {fpy2-0.0.2/fpy2/runtime/sampling → fpy2-0.0.4/fpy2/sample}/sample.py +20 -12
- {fpy2-0.0.2/fpy2/runtime/sampling → fpy2-0.0.4/fpy2/sample}/table.py +21 -21
- fpy2-0.0.4/fpy2/transform/__init__.py +13 -0
- fpy2-0.0.4/fpy2/transform/context_inline.py +118 -0
- fpy2-0.0.4/fpy2/transform/copy_propagate.py +76 -0
- fpy2-0.0.4/fpy2/transform/for_bundling.py +177 -0
- fpy2-0.0.4/fpy2/transform/for_unpack.py +69 -0
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.4/fpy2/transform}/func_update.py +15 -17
- fpy2-0.0.4/fpy2/transform/if_bundling.py +254 -0
- fpy2-0.0.4/fpy2/transform/rename_target.py +95 -0
- fpy2-0.0.4/fpy2/transform/simplify_if.py +185 -0
- fpy2-0.0.4/fpy2/transform/while_bundling.py +136 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/typing/__init__.pyi +3 -9
- fpy2-0.0.4/fpy2/utils/__init__.py +27 -0
- fpy2-0.0.4/fpy2/utils/bits.py +26 -0
- fpy2-0.0.4/fpy2/utils/decorator.py +105 -0
- fpy2-0.0.4/fpy2/utils/float_params.py +30 -0
- fpy2-0.0.4/fpy2/utils/fractions.py +136 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/utils/gensym.py +9 -1
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/utils/identifier.py +33 -2
- fpy2-0.0.4/fpy2/utils/inspect.py +15 -0
- fpy2-0.0.4/fpy2/utils/iterator.py +15 -0
- fpy2-0.0.4/fpy2/utils/ordering.py +39 -0
- fpy2-0.0.4/fpy2.egg-info/PKG-INFO +113 -0
- fpy2-0.0.4/fpy2.egg-info/SOURCES.txt +86 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2.egg-info/requires.txt +3 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/pyproject.toml +8 -3
- fpy2-0.0.2/PKG-INFO +0 -39
- fpy2-0.0.2/README.md +0 -18
- fpy2-0.0.2/fpy2/__init__.py +0 -19
- fpy2-0.0.2/fpy2/backend/fpc.py +0 -480
- fpy2-0.0.2/fpy2/backend/fpy.py +0 -298
- fpy2-0.0.2/fpy2/frontend/__init__.py +0 -13
- fpy2-0.0.2/fpy2/frontend/codegen.py +0 -278
- fpy2-0.0.2/fpy2/frontend/decorator.py +0 -106
- fpy2-0.0.2/fpy2/frontend/definition.py +0 -149
- fpy2-0.0.2/fpy2/frontend/fpyast.py +0 -655
- fpy2-0.0.2/fpy2/frontend/live_vars.py +0 -201
- fpy2-0.0.2/fpy2/frontend/syntax_check.py +0 -294
- fpy2-0.0.2/fpy2/frontend/visitor.py +0 -208
- fpy2-0.0.2/fpy2/ir/__init__.py +0 -13
- fpy2-0.0.2/fpy2/ir/formatter.py +0 -269
- fpy2-0.0.2/fpy2/ir/ir.py +0 -739
- fpy2-0.0.2/fpy2/ir/types.py +0 -29
- fpy2-0.0.2/fpy2/ir/visitor.py +0 -578
- fpy2-0.0.2/fpy2/passes/__init__.py +0 -9
- fpy2-0.0.2/fpy2/passes/define_use.py +0 -105
- fpy2-0.0.2/fpy2/passes/for_bundling.py +0 -109
- fpy2-0.0.2/fpy2/passes/reaching_defs.py +0 -129
- fpy2-0.0.2/fpy2/passes/simplify_if.py +0 -109
- fpy2-0.0.2/fpy2/passes/ssa.py +0 -330
- fpy2-0.0.2/fpy2/passes/verify.py +0 -221
- fpy2-0.0.2/fpy2/passes/while_bundling.py +0 -125
- fpy2-0.0.2/fpy2/runtime/__init__.py +0 -15
- fpy2-0.0.2/fpy2/runtime/common.py +0 -12
- fpy2-0.0.2/fpy2/runtime/env.py +0 -23
- fpy2-0.0.2/fpy2/runtime/function.py +0 -110
- fpy2-0.0.2/fpy2/runtime/metric.py +0 -32
- fpy2-0.0.2/fpy2/runtime/native/__init__.py +0 -1
- fpy2-0.0.2/fpy2/runtime/native/interpreter.py +0 -512
- fpy2-0.0.2/fpy2/runtime/profile/__init__.py +0 -2
- fpy2-0.0.2/fpy2/runtime/profile/expr_profiler.py +0 -240
- fpy2-0.0.2/fpy2/runtime/profile/function_profiler.py +0 -138
- fpy2-0.0.2/fpy2/runtime/real/__init__.py +0 -2
- fpy2-0.0.2/fpy2/runtime/real/interpreter.py +0 -583
- fpy2-0.0.2/fpy2/runtime/real/interval.py +0 -61
- fpy2-0.0.2/fpy2/runtime/real/rival_manager.py +0 -142
- fpy2-0.0.2/fpy2/runtime/titanic/__init__.py +0 -1
- fpy2-0.0.2/fpy2/runtime/titanic/interpreter.py +0 -616
- fpy2-0.0.2/fpy2/utils/__init__.py +0 -10
- fpy2-0.0.2/fpy2/utils/defaults.py +0 -11
- fpy2-0.0.2/fpy2/utils/fraction.py +0 -55
- fpy2-0.0.2/fpy2.egg-info/PKG-INFO +0 -39
- fpy2-0.0.2/fpy2.egg-info/SOURCES.txt +0 -68
- {fpy2-0.0.2 → fpy2-0.0.4}/LICENSE +0 -0
- {fpy2-0.0.2/fpy2/runtime/sampling → fpy2-0.0.4/fpy2/sample}/__init__.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/typing/__init__.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/utils/compare.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/utils/error.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/utils/location.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2/utils/string.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2.egg-info/dependency_links.txt +0 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/fpy2.egg-info/top_level.txt +0 -0
- {fpy2-0.0.2 → fpy2-0.0.4}/setup.cfg +0 -0
fpy2-0.0.4/PKG-INFO
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fpy2
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: An embedded DSL for numerical computing
|
|
5
|
+
Author-email: Brett Saiki <bsaiki@cs.washington.edu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bksaiki/fpy
|
|
8
|
+
Project-URL: Issues, https://github.com/bksaiki/fpy/issues
|
|
9
|
+
Project-URL: Documentation, https://fpy.readthedocs.io/
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: titanfp==0.1.1
|
|
16
|
+
Requires-Dist: gmpy2==2.2
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: mypy; extra == "dev"
|
|
19
|
+
Requires-Dist: ruff; extra == "dev"
|
|
20
|
+
Requires-Dist: build; extra == "dev"
|
|
21
|
+
Requires-Dist: twine; extra == "dev"
|
|
22
|
+
Requires-Dist: sphinx; extra == "dev"
|
|
23
|
+
Requires-Dist: sphinx-rtd-theme; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# FPy
|
|
27
|
+
|
|
28
|
+
An embedded Python DSL for specifying and simulating numerical algorithms.
|
|
29
|
+
|
|
30
|
+
Important links:
|
|
31
|
+
- PyPI package: [fpy2](https://pypi.org/project/fpy2/)
|
|
32
|
+
- Documentation: [fpy.readthedocs.io](https://fpy.readthedocs.io/)
|
|
33
|
+
- GitHub: [fpy](https://github.com/bksaiki/fpy)
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Requirements:
|
|
38
|
+
- Python 3.11 or later
|
|
39
|
+
|
|
40
|
+
The following instructions assume a `bash`-like shell.
|
|
41
|
+
If you do not have a Python virtual environment,
|
|
42
|
+
create one using
|
|
43
|
+
```bash
|
|
44
|
+
python3 -m venv .env/
|
|
45
|
+
```
|
|
46
|
+
and activate it using using
|
|
47
|
+
```bash
|
|
48
|
+
source .env/bin/activate
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
To install a _frozen_ instance of FPy, run:
|
|
52
|
+
```bash
|
|
53
|
+
pip install .
|
|
54
|
+
```
|
|
55
|
+
or with `make`, run
|
|
56
|
+
```bash
|
|
57
|
+
make install
|
|
58
|
+
```
|
|
59
|
+
Note that this will not install the necessary dependencies for
|
|
60
|
+
development and installs a copy of the `fpy2` package.
|
|
61
|
+
If you checkout a different commit or branch, you will
|
|
62
|
+
need to reinstall FPy to overwrite the previous version.
|
|
63
|
+
|
|
64
|
+
To uninstall FPy, run:
|
|
65
|
+
```bash
|
|
66
|
+
pip uninstall fpy2
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Development
|
|
70
|
+
|
|
71
|
+
Developers of FPy should read this section since
|
|
72
|
+
installing FPy is actually different.
|
|
73
|
+
|
|
74
|
+
Requirements:
|
|
75
|
+
- Python 3.11 or later
|
|
76
|
+
- `make`
|
|
77
|
+
|
|
78
|
+
### Installation
|
|
79
|
+
|
|
80
|
+
If you do not have a Python virtual environment,
|
|
81
|
+
create one using
|
|
82
|
+
```bash
|
|
83
|
+
python3 -m venv .env/
|
|
84
|
+
```
|
|
85
|
+
and activate it using using
|
|
86
|
+
```bash
|
|
87
|
+
source .env/bin/activate
|
|
88
|
+
```
|
|
89
|
+
To install an instance of FPy for development, run:
|
|
90
|
+
```bash
|
|
91
|
+
pip install -e .[dev]
|
|
92
|
+
```
|
|
93
|
+
or with `make`, run
|
|
94
|
+
```bash
|
|
95
|
+
make install-dev
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Testing
|
|
99
|
+
|
|
100
|
+
There are a number of tests that can be run through
|
|
101
|
+
the `Makefile` including
|
|
102
|
+
```bash
|
|
103
|
+
make lint
|
|
104
|
+
```
|
|
105
|
+
to ensure formatting and type safety;
|
|
106
|
+
```bash
|
|
107
|
+
make unittest
|
|
108
|
+
```
|
|
109
|
+
to run the unit tests;
|
|
110
|
+
```bash
|
|
111
|
+
make infratest
|
|
112
|
+
```
|
|
113
|
+
to run the infrastructure tests.
|
fpy2-0.0.4/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# FPy
|
|
2
|
+
|
|
3
|
+
An embedded Python DSL for specifying and simulating numerical algorithms.
|
|
4
|
+
|
|
5
|
+
Important links:
|
|
6
|
+
- PyPI package: [fpy2](https://pypi.org/project/fpy2/)
|
|
7
|
+
- Documentation: [fpy.readthedocs.io](https://fpy.readthedocs.io/)
|
|
8
|
+
- GitHub: [fpy](https://github.com/bksaiki/fpy)
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Requirements:
|
|
13
|
+
- Python 3.11 or later
|
|
14
|
+
|
|
15
|
+
The following instructions assume a `bash`-like shell.
|
|
16
|
+
If you do not have a Python virtual environment,
|
|
17
|
+
create one using
|
|
18
|
+
```bash
|
|
19
|
+
python3 -m venv .env/
|
|
20
|
+
```
|
|
21
|
+
and activate it using using
|
|
22
|
+
```bash
|
|
23
|
+
source .env/bin/activate
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
To install a _frozen_ instance of FPy, run:
|
|
27
|
+
```bash
|
|
28
|
+
pip install .
|
|
29
|
+
```
|
|
30
|
+
or with `make`, run
|
|
31
|
+
```bash
|
|
32
|
+
make install
|
|
33
|
+
```
|
|
34
|
+
Note that this will not install the necessary dependencies for
|
|
35
|
+
development and installs a copy of the `fpy2` package.
|
|
36
|
+
If you checkout a different commit or branch, you will
|
|
37
|
+
need to reinstall FPy to overwrite the previous version.
|
|
38
|
+
|
|
39
|
+
To uninstall FPy, run:
|
|
40
|
+
```bash
|
|
41
|
+
pip uninstall fpy2
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
Developers of FPy should read this section since
|
|
47
|
+
installing FPy is actually different.
|
|
48
|
+
|
|
49
|
+
Requirements:
|
|
50
|
+
- Python 3.11 or later
|
|
51
|
+
- `make`
|
|
52
|
+
|
|
53
|
+
### Installation
|
|
54
|
+
|
|
55
|
+
If you do not have a Python virtual environment,
|
|
56
|
+
create one using
|
|
57
|
+
```bash
|
|
58
|
+
python3 -m venv .env/
|
|
59
|
+
```
|
|
60
|
+
and activate it using using
|
|
61
|
+
```bash
|
|
62
|
+
source .env/bin/activate
|
|
63
|
+
```
|
|
64
|
+
To install an instance of FPy for development, run:
|
|
65
|
+
```bash
|
|
66
|
+
pip install -e .[dev]
|
|
67
|
+
```
|
|
68
|
+
or with `make`, run
|
|
69
|
+
```bash
|
|
70
|
+
make install-dev
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Testing
|
|
74
|
+
|
|
75
|
+
There are a number of tests that can be run through
|
|
76
|
+
the `Makefile` including
|
|
77
|
+
```bash
|
|
78
|
+
make lint
|
|
79
|
+
```
|
|
80
|
+
to ensure formatting and type safety;
|
|
81
|
+
```bash
|
|
82
|
+
make unittest
|
|
83
|
+
```
|
|
84
|
+
to run the unit tests;
|
|
85
|
+
```bash
|
|
86
|
+
make infratest
|
|
87
|
+
```
|
|
88
|
+
to run the infrastructure tests.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FPy is a library for simulating numerical programs
|
|
3
|
+
with many different number systems.
|
|
4
|
+
|
|
5
|
+
It provides an embedded DSL for specifying programs via its `@fpy` decorator.
|
|
6
|
+
The language has a runtime that can simulate programs
|
|
7
|
+
under different number systems and compilers to other languages.
|
|
8
|
+
|
|
9
|
+
The numbers library supports many different number types including:
|
|
10
|
+
|
|
11
|
+
- multiprecision floating point (`MPFloatContext`)
|
|
12
|
+
- multiprecision floatingpoint with subnormalization (`MPSFloatContext`)
|
|
13
|
+
- bounded, multiprecision floating point (`MPBFloatContext`)
|
|
14
|
+
- IEEE 754 floating point (`IEEEContext`)
|
|
15
|
+
|
|
16
|
+
These number systems guarantee correct rounding via MPFR.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from .number import (
|
|
20
|
+
# number types
|
|
21
|
+
Float,
|
|
22
|
+
RealFloat,
|
|
23
|
+
# abstract context types
|
|
24
|
+
Context,
|
|
25
|
+
OrdinalContext,
|
|
26
|
+
SizedContext,
|
|
27
|
+
EncodableContext,
|
|
28
|
+
# concrete context types
|
|
29
|
+
ExtFloatContext,
|
|
30
|
+
FixedContext,
|
|
31
|
+
MPFixedContext,
|
|
32
|
+
MPFloatContext,
|
|
33
|
+
MPBFixedContext,
|
|
34
|
+
MPBFloatContext,
|
|
35
|
+
MPSFloatContext,
|
|
36
|
+
IEEEContext,
|
|
37
|
+
RealContext,
|
|
38
|
+
# rounding utilities
|
|
39
|
+
RoundingMode,
|
|
40
|
+
RoundingDirection, RM,
|
|
41
|
+
# encoding utilities
|
|
42
|
+
ExtFloatNanKind,
|
|
43
|
+
FixedOverflowKind, OF,
|
|
44
|
+
# type aliases
|
|
45
|
+
FP256, FP128, FP64, FP32, FP16,
|
|
46
|
+
TF32, BF16,
|
|
47
|
+
S1E5M2, S1E4M3,
|
|
48
|
+
MX_E5M2, MX_E4M3, MX_E3M2, MX_E2M3, MX_E2M1,
|
|
49
|
+
FP8P1, FP8P2, FP8P3, FP8P4, FP8P5, FP8P6, FP8P7,
|
|
50
|
+
INTEGER,
|
|
51
|
+
SINT8, SINT16, SINT32, SINT64,
|
|
52
|
+
UINT8, UINT16, UINT32, UINT64
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
from .fpc_context import FPCoreContext, NoSuchContextError
|
|
56
|
+
|
|
57
|
+
from .decorator import fpy, pattern, fpy_prim
|
|
58
|
+
|
|
59
|
+
from .backend import (
|
|
60
|
+
Backend,
|
|
61
|
+
FPCoreCompiler
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
from .interpret import (
|
|
65
|
+
Interpreter,
|
|
66
|
+
PythonInterpreter,
|
|
67
|
+
DefaultInterpreter,
|
|
68
|
+
set_default_interpreter,
|
|
69
|
+
get_default_interpreter,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
from .function import Function
|
|
73
|
+
from .env import ForeignEnv
|
|
74
|
+
|
|
75
|
+
from .utils import (
|
|
76
|
+
fraction,
|
|
77
|
+
digits_to_fraction as digits,
|
|
78
|
+
decnum_to_fraction as decnum,
|
|
79
|
+
hexnum_to_fraction as hexnum,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
###########################################################
|
|
83
|
+
# Re-exports
|
|
84
|
+
|
|
85
|
+
from titanfp.titanic.ndarray import NDArray
|
|
86
|
+
|
|
87
|
+
###########################################################
|
|
88
|
+
# typing hints
|
|
89
|
+
|
|
90
|
+
import typing
|
|
91
|
+
|
|
92
|
+
from typing import Literal as Dim
|
|
93
|
+
|
|
94
|
+
_Dims = typing.TypeVarTuple('_Dims')
|
|
95
|
+
_DType = typing.TypeVar('_DType')
|
|
96
|
+
|
|
97
|
+
class Tensor(tuple, typing.Generic[*_Dims, _DType]):
|
|
98
|
+
"""
|
|
99
|
+
FPy type hint for a homogenous tensor object::
|
|
100
|
+
|
|
101
|
+
from fpy2 import Tensor, Real
|
|
102
|
+
from typing import TypeAlias
|
|
103
|
+
|
|
104
|
+
MatrixN: TypeAlias = Tensor[Literal['N', 'N'], Real]
|
|
105
|
+
Matrix3: TypeAlias = Tensor[Literal[3, 3], Real]
|
|
106
|
+
|
|
107
|
+
Tensors have fixed or symbolic sizes and a uniform scalar data type.
|
|
108
|
+
|
|
109
|
+
Values of this type should not be constructed directly.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def __init__(self, *args, **kwargs):
|
|
113
|
+
super().__init__(*args, **kwargs)
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Definition use analysis for FPy ASTs"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TypeAlias, Union
|
|
5
|
+
|
|
6
|
+
from ..ast.fpyast import *
|
|
7
|
+
from ..ast.visitor import DefaultVisitor
|
|
8
|
+
from ..utils import default_repr
|
|
9
|
+
|
|
10
|
+
Definition: TypeAlias = Argument | Stmt | CompExpr
|
|
11
|
+
|
|
12
|
+
@default_repr
|
|
13
|
+
class _DefineUnion:
|
|
14
|
+
"""Union of possible definition sites."""
|
|
15
|
+
defs: set[Definition]
|
|
16
|
+
|
|
17
|
+
def __init__(self, defs: set[Definition]):
|
|
18
|
+
self.defs = set(defs)
|
|
19
|
+
|
|
20
|
+
def __eq__(self, other):
|
|
21
|
+
if not isinstance(other, _DefineUnion):
|
|
22
|
+
return NotImplemented
|
|
23
|
+
return self.defs == other.defs
|
|
24
|
+
|
|
25
|
+
def __hash__(self):
|
|
26
|
+
return hash(tuple(self.defs))
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def union(*defs_or_unions: Union[Definition, '_DefineUnion']):
|
|
30
|
+
"""Create a union of definitions from a set of definitions or unions."""
|
|
31
|
+
defs: set[Definition] = set()
|
|
32
|
+
for item in defs_or_unions:
|
|
33
|
+
if isinstance(item, _DefineUnion):
|
|
34
|
+
defs.update(item.defs)
|
|
35
|
+
else:
|
|
36
|
+
defs.add(item)
|
|
37
|
+
|
|
38
|
+
if len(defs) == 0:
|
|
39
|
+
raise ValueError('Cannot create a union of an empty set of definitions')
|
|
40
|
+
elif len(defs) == 1:
|
|
41
|
+
return defs.pop()
|
|
42
|
+
else:
|
|
43
|
+
return _DefineUnion(defs)
|
|
44
|
+
|
|
45
|
+
class DefinitionCtx(dict[NamedId, Definition | _DefineUnion]):
|
|
46
|
+
"""Mapping from variable to its definition (or possible definitions)."""
|
|
47
|
+
|
|
48
|
+
def copy(self) -> 'DefinitionCtx':
|
|
49
|
+
"""Returns a shallow copy of the context."""
|
|
50
|
+
return DefinitionCtx(self)
|
|
51
|
+
|
|
52
|
+
def mutated_in(self, other: 'DefinitionCtx') -> list[NamedId]:
|
|
53
|
+
"""
|
|
54
|
+
Returns the set of variables that are defined in `self`
|
|
55
|
+
and mutated in `other`.
|
|
56
|
+
"""
|
|
57
|
+
names: set[NamedId] = set()
|
|
58
|
+
for name in self.keys() & other.keys():
|
|
59
|
+
if self[name] != other[name]:
|
|
60
|
+
names.add(name)
|
|
61
|
+
return list(names)
|
|
62
|
+
|
|
63
|
+
def fresh_in(self, other: 'DefinitionCtx') -> set[NamedId]:
|
|
64
|
+
"""
|
|
65
|
+
Returns the set of variables that are defined in `other`
|
|
66
|
+
but not in `self`.
|
|
67
|
+
"""
|
|
68
|
+
return set(other.keys() - self.keys())
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class DefineUseAnalysis:
|
|
73
|
+
"""Result of definition-use analysis"""
|
|
74
|
+
defs: dict[NamedId, set[Definition]]
|
|
75
|
+
uses: dict[Definition, set[Var | IndexedAssign]]
|
|
76
|
+
stmts: dict[Stmt, tuple[DefinitionCtx, DefinitionCtx]]
|
|
77
|
+
blocks: dict[StmtBlock, tuple[DefinitionCtx, DefinitionCtx]]
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def default():
|
|
81
|
+
"""Default analysis with empty definitions and uses"""
|
|
82
|
+
return DefineUseAnalysis({}, {}, {}, {})
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def names(self) -> set[NamedId]:
|
|
86
|
+
"""Returns the set of all variable names in the analysis"""
|
|
87
|
+
return set(self.defs.keys())
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class _DefineUseInstance(DefaultVisitor):
|
|
91
|
+
"""Per-IR instance of definition-use analysis"""
|
|
92
|
+
ast: FuncDef | StmtBlock
|
|
93
|
+
analysis: DefineUseAnalysis
|
|
94
|
+
|
|
95
|
+
def __init__(self, ast: FuncDef | StmtBlock):
|
|
96
|
+
self.ast = ast
|
|
97
|
+
self.analysis = DefineUseAnalysis.default()
|
|
98
|
+
|
|
99
|
+
def analyze(self):
|
|
100
|
+
match self.ast:
|
|
101
|
+
case FuncDef():
|
|
102
|
+
self._visit_function(self.ast, DefinitionCtx())
|
|
103
|
+
case StmtBlock():
|
|
104
|
+
self._visit_block(self.ast, DefinitionCtx())
|
|
105
|
+
case _:
|
|
106
|
+
raise RuntimeError(f'unreachable case: {self.ast}')
|
|
107
|
+
return self.analysis
|
|
108
|
+
|
|
109
|
+
def _add_def(self, name: NamedId, definition: Definition):
|
|
110
|
+
if name not in self.analysis.defs:
|
|
111
|
+
self.analysis.defs[name] = set()
|
|
112
|
+
self.analysis.defs[name].add(definition)
|
|
113
|
+
self.analysis.uses[definition] = set()
|
|
114
|
+
|
|
115
|
+
def _add_use(self, name: NamedId, use: Var | IndexedAssign, ctx: DefinitionCtx):
|
|
116
|
+
def_or_union = ctx[name]
|
|
117
|
+
if isinstance(def_or_union, _DefineUnion):
|
|
118
|
+
for def_ in def_or_union.defs:
|
|
119
|
+
self.analysis.uses[def_].add(use)
|
|
120
|
+
else:
|
|
121
|
+
self.analysis.uses[def_or_union].add(use)
|
|
122
|
+
|
|
123
|
+
def _visit_var(self, e: Var, ctx: DefinitionCtx):
|
|
124
|
+
if e.name not in ctx:
|
|
125
|
+
raise NotImplementedError(f'undefined variable {e.name}')
|
|
126
|
+
self._add_use(e.name, e, ctx)
|
|
127
|
+
|
|
128
|
+
def _visit_comp_expr(self, e: CompExpr, ctx: DefinitionCtx):
|
|
129
|
+
for iterable in e.iterables:
|
|
130
|
+
self._visit_expr(iterable, ctx)
|
|
131
|
+
ctx = ctx.copy()
|
|
132
|
+
for target in e.targets:
|
|
133
|
+
for name in target.names():
|
|
134
|
+
self._add_def(name, e)
|
|
135
|
+
ctx[name] = e
|
|
136
|
+
self._visit_expr(e.elt, ctx)
|
|
137
|
+
|
|
138
|
+
def _visit_assign(self, stmt: Assign, ctx: DefinitionCtx):
|
|
139
|
+
self._visit_expr(stmt.expr, ctx)
|
|
140
|
+
for var in stmt.binding.names():
|
|
141
|
+
self._add_def(var, stmt)
|
|
142
|
+
ctx[var] = stmt
|
|
143
|
+
|
|
144
|
+
def _visit_indexed_assign(self, stmt: IndexedAssign, ctx: DefinitionCtx):
|
|
145
|
+
self._add_use(stmt.var, stmt, ctx)
|
|
146
|
+
for slice in stmt.slices:
|
|
147
|
+
self._visit_expr(slice, ctx)
|
|
148
|
+
self._visit_expr(stmt.expr, ctx)
|
|
149
|
+
|
|
150
|
+
def _visit_if1(self, stmt: If1Stmt, ctx: DefinitionCtx):
|
|
151
|
+
self._visit_expr(stmt.cond, ctx)
|
|
152
|
+
body_ctx = self._visit_block(stmt.body, ctx.copy())
|
|
153
|
+
# merge contexts along both paths
|
|
154
|
+
# definitions cannot be introduced in the body
|
|
155
|
+
for var in ctx:
|
|
156
|
+
ctx[var] = _DefineUnion.union(ctx[var], body_ctx[var])
|
|
157
|
+
|
|
158
|
+
def _visit_if(self, stmt: IfStmt, ctx: DefinitionCtx):
|
|
159
|
+
self._visit_expr(stmt.cond, ctx)
|
|
160
|
+
ift_ctx = self._visit_block(stmt.ift, ctx.copy())
|
|
161
|
+
iff_ctx = self._visit_block(stmt.iff, ctx.copy())
|
|
162
|
+
# merge contexts along both paths
|
|
163
|
+
for var in ift_ctx.keys() & iff_ctx.keys():
|
|
164
|
+
ctx[var] = _DefineUnion.union(ift_ctx[var], iff_ctx[var])
|
|
165
|
+
|
|
166
|
+
def _visit_while(self, stmt: WhileStmt, ctx: DefinitionCtx):
|
|
167
|
+
self._visit_expr(stmt.cond, ctx)
|
|
168
|
+
body_ctx = self._visit_block(stmt.body, ctx.copy())
|
|
169
|
+
# merge contexts along both paths
|
|
170
|
+
# definitions cannot be introduced in the body
|
|
171
|
+
for var in ctx:
|
|
172
|
+
ctx[var] = _DefineUnion.union(ctx[var], body_ctx[var])
|
|
173
|
+
|
|
174
|
+
def _visit_for(self, stmt: ForStmt, ctx: DefinitionCtx):
|
|
175
|
+
self._visit_expr(stmt.iterable, ctx)
|
|
176
|
+
body_ctx = ctx.copy()
|
|
177
|
+
match stmt.target:
|
|
178
|
+
case NamedId():
|
|
179
|
+
self._add_def(stmt.target, stmt)
|
|
180
|
+
body_ctx[stmt.target] = stmt
|
|
181
|
+
case TupleBinding():
|
|
182
|
+
for var in stmt.target.names():
|
|
183
|
+
self._add_def(var, stmt)
|
|
184
|
+
body_ctx[var] = stmt
|
|
185
|
+
|
|
186
|
+
body_ctx = self._visit_block(stmt.body, body_ctx)
|
|
187
|
+
# merge contexts along both paths
|
|
188
|
+
# definitions cannot be introduced in the body
|
|
189
|
+
for var in ctx:
|
|
190
|
+
ctx[var] = _DefineUnion.union(ctx[var], body_ctx[var])
|
|
191
|
+
|
|
192
|
+
def _visit_statement(self, stmt: Stmt, ctx: DefinitionCtx):
|
|
193
|
+
ctx_in = ctx.copy()
|
|
194
|
+
super()._visit_statement(stmt, ctx)
|
|
195
|
+
self.analysis.stmts[stmt] = (ctx_in, ctx.copy())
|
|
196
|
+
|
|
197
|
+
def _visit_block(self, block: StmtBlock, ctx: DefinitionCtx):
|
|
198
|
+
ctx_in = ctx.copy()
|
|
199
|
+
for stmt in block.stmts:
|
|
200
|
+
self._visit_statement(stmt, ctx)
|
|
201
|
+
self.analysis.blocks[block] = (ctx_in, ctx.copy())
|
|
202
|
+
return ctx
|
|
203
|
+
|
|
204
|
+
def _visit_function(self, func: FuncDef, ctx: DefinitionCtx):
|
|
205
|
+
for arg in func.args:
|
|
206
|
+
if isinstance(arg.name, NamedId):
|
|
207
|
+
self._add_def(arg.name, arg)
|
|
208
|
+
ctx[arg.name] = arg
|
|
209
|
+
self._visit_block(func.body, ctx.copy())
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class DefineUse:
|
|
213
|
+
"""
|
|
214
|
+
Definition-use analyzer for the FPy IR.
|
|
215
|
+
|
|
216
|
+
Computes definition-use chains for each variable.
|
|
217
|
+
|
|
218
|
+
name ---> definition ---> use1, use2, ...
|
|
219
|
+
---> definition ---> use1, use2, ...
|
|
220
|
+
...
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def analyze(ast: FuncDef | StmtBlock):
|
|
225
|
+
if not isinstance(ast, FuncDef | StmtBlock):
|
|
226
|
+
raise TypeError(f'Expected \'FuncDef\' or \'StmtBlock\', got {type(ast)} for {ast}')
|
|
227
|
+
return _DefineUseInstance(ast).analyze()
|