├── islpy ├── py.typed ├── version.py ├── __init__.py └── _monkeypatch.py ├── doc ├── .gitignore ├── images │ ├── after-union.png │ └── before-union.png ├── upload-docs.sh ├── ref_set.rst ├── ref_geo.rst ├── ref_schedule.rst ├── ref_flow.rst ├── ref_ast.rst ├── ref_expr.rst ├── conf.py ├── index.rst ├── ref_containers.rst ├── ref_fundamental.rst ├── Makefile ├── reference.rst └── misc.rst ├── examples ├── .gitignore └── demo.py ├── isl-supplementary ├── gitversion.h ├── isl │ ├── config.h │ └── stdint.h └── isl_config.h ├── .gitmodules ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── autopush.yml │ ├── wheels.yml │ └── ci.yml ├── .editorconfig ├── .gitignore ├── CITATION.cff ├── src └── wrapper │ ├── wrap_isl_part3.cpp │ ├── wrap_helpers.hpp │ ├── wrap_isl_part2.cpp │ ├── wrap_isl_part1.cpp │ ├── wrap_isl.cpp │ └── wrap_isl.hpp ├── .gitlab-ci.yml ├── README.rst ├── stubgen └── stubgen.py ├── pyproject.toml ├── CMakeLists.txt ├── test └── test_isl.py └── .basedpyright └── baseline.json /islpy/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.png 2 | -------------------------------------------------------------------------------- /isl-supplementary/gitversion.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /isl-supplementary/isl/config.h: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /isl-supplementary/isl/stdint.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "isl"] 2 | path = isl 3 | url = https://github.com/inducer/isl.git 4 | -------------------------------------------------------------------------------- /doc/images/after-union.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inducer/islpy/HEAD/doc/images/after-union.png -------------------------------------------------------------------------------- /doc/images/before-union.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inducer/islpy/HEAD/doc/images/before-union.png -------------------------------------------------------------------------------- /doc/upload-docs.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | rsync --verbose --archive --delete _build/html/ doc-upload:doc/islpy 4 | -------------------------------------------------------------------------------- /isl-supplementary/isl_config.h: -------------------------------------------------------------------------------- 1 | #define HAVE_DECL_MP_GET_MEMORY_FUNCTIONS 1 2 | #define WARN_UNUSED /* nothing */ 3 | #ifdef __GNUC__ 4 | #define HAVE_DECL___BUILTIN_FFS 1 5 | #else 6 | #define HAVE_DECL_FFS 1 7 | #endif 8 | 9 | #include 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Set update schedule for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | groups: 9 | actions: 10 | patterns: 11 | - "*" 12 | 13 | # vim: sw=4 14 | -------------------------------------------------------------------------------- /islpy/version.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import re 4 | from importlib import metadata 5 | 6 | 7 | VERSION_TEXT = metadata.version("islpy") 8 | _match = re.match(r"^([0-9.]+)([a-z0-9]*?)$", VERSION_TEXT) 9 | assert _match is not None 10 | VERSION_STATUS = _match.group(2) 11 | VERSION = tuple(int(nr) for nr in _match.group(1).split(".")) 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: ❓ Question 4 | url: https://github.com/inducer/islpy/discussions/categories/q-a 5 | about: Ask and answer questions about islpy on Discussions 6 | - name: 🔧 Troubleshooting 7 | url: https://github.com/inducer/islpy/discussions/categories/troubleshooting 8 | about: For troubleshooting help, see the Discussions 9 | -------------------------------------------------------------------------------- /doc/ref_set.rst: -------------------------------------------------------------------------------- 1 | Reference: Sets and Maps 2 | ======================== 3 | 4 | .. currentmodule:: islpy 5 | 6 | Basic Set 7 | --------- 8 | 9 | .. autoclass:: BasicSet 10 | :members: 11 | 12 | Basic Map 13 | --------- 14 | 15 | .. autoclass:: BasicMap 16 | :members: 17 | 18 | Set 19 | --- 20 | 21 | .. autoclass:: Set 22 | :members: 23 | 24 | Map 25 | --- 26 | 27 | .. autoclass:: Map 28 | :members: 29 | 30 | Union Set 31 | --------- 32 | 33 | .. autoclass:: UnionSet 34 | :members: 35 | 36 | Union Map 37 | --------- 38 | 39 | .. autoclass:: UnionMap 40 | :members: 41 | 42 | 43 | -------------------------------------------------------------------------------- /doc/ref_geo.rst: -------------------------------------------------------------------------------- 1 | Reference: Geometric Entities 2 | ============================= 3 | 4 | .. currentmodule:: islpy 5 | 6 | Point 7 | ----- 8 | 9 | .. autoclass:: Point 10 | :members: 11 | 12 | Vertex 13 | ------ 14 | 15 | .. autoclass:: Vertex 16 | :members: 17 | 18 | Vertices 19 | -------- 20 | 21 | .. autoclass:: Vertices 22 | :members: 23 | 24 | StrideInfo 25 | ---------- 26 | 27 | .. autoclass:: StrideInfo 28 | :members: 29 | 30 | Cell 31 | ---- 32 | 33 | .. autoclass:: Cell 34 | :members: 35 | 36 | Fixed Box 37 | --------- 38 | 39 | .. autoclass:: FixedBox 40 | :members: 41 | 42 | 43 | -------------------------------------------------------------------------------- /.github/workflows/autopush.yml: -------------------------------------------------------------------------------- 1 | name: Gitlab mirror 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | autopush: 9 | name: Automatic push to gitlab.tiker.net 10 | if: startsWith(github.repository, 'inducer/') 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v6 14 | - run: | 15 | curl -L -O https://tiker.net/ci-support-v0 16 | . ./ci-support-v0 17 | mirror_github_to_gitlab 18 | 19 | env: 20 | GITLAB_AUTOPUSH_KEY: ${{ secrets.GITLAB_AUTOPUSH_KEY }} 21 | 22 | # vim: sw=4 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org/ 2 | # https://github.com/editorconfig/editorconfig-vim 3 | # https://github.com/editorconfig/editorconfig-emacs 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.py] 15 | indent_size = 4 16 | 17 | [*.rst] 18 | indent_size = 4 19 | 20 | [*.cpp] 21 | indent_size = 2 22 | 23 | [*.hpp] 24 | indent_size = 2 25 | 26 | # There may be one in doc/ 27 | [Makefile] 28 | indent_style = tab 29 | 30 | # https://github.com/microsoft/vscode/issues/1679 31 | [*.md] 32 | trim_trailing_whitespace = false 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Environment (please complete the following information):** 24 | - OS: [e.g. Linux] 25 | - Python version: [e.g. 3.10] 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pydevproject 2 | .project 3 | .settings 4 | *~ 5 | .*.sw[po] 6 | .sw[po] 7 | *.dat 8 | *.pyc 9 | build 10 | *.prof 11 | siteconf.py 12 | doc/hedge-notes.pdf 13 | *.vtk 14 | *.silo 15 | *.session 16 | dump.py 17 | *.orig 18 | /Makefile 19 | tags 20 | *.vtu 21 | *.pvtu 22 | *.pvd 23 | doc/user-reference 24 | doc/dev-reference 25 | *.poly 26 | *.node 27 | *.bak 28 | *.pdf 29 | *.tif 30 | *.so 31 | *.pyd 32 | *.mpeg 33 | *-journal 34 | visitlog.py 35 | *.log 36 | .figleaf 37 | dist 38 | *.egg* 39 | MANIFEST 40 | *.patch 41 | *.LOCAL.[0-9]* 42 | *.REMOTE.[0-9]* 43 | *.BASE.[0-9]* 44 | tmp 45 | temp* 46 | setuptools.pth 47 | setuptools-*.tar.gz 48 | core 49 | src/wrapper/gen-* 50 | .dirty-git-ok 51 | 52 | # wheels 53 | arch_tmp 54 | archives 55 | downloads 56 | gmp-* 57 | isl-* 58 | wheelhouse 59 | venv 60 | 61 | _skbuild/ 62 | libnanobind-static.a 63 | CMakeFiles/ 64 | 65 | preproc-headers 66 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Kloeckner" 5 | given-names: "Andreas" 6 | orcid: "https://orcid.org/0000-0003-1228-519X" 7 | - family-names: "Fernando" 8 | given-names: "Isuru" 9 | - family-names: "Yang" 10 | given-names: "Cambridge" 11 | - family-names: "Kulkarni" 12 | given-names: "Kaushik" 13 | # Disabled pending https://github.com/zenodo/zenodo/issues/2343 14 | # - alias: "iasakura" 15 | - family-names: "Almahallawi" 16 | given-names: "Deyaaeldeen" 17 | - family-names: "Gao" 18 | given-names: "Hao" 19 | - family-names: "Stevens" 20 | given-names: "James" 21 | - family-names: "Fikl" 22 | given-names: "Alex" 23 | - family-names: "Wala" 24 | given-names: "Matt" 25 | # Disabled pending https://github.com/zenodo/zenodo/issues/2343 26 | # - alias: "bhuztez" 27 | 28 | title: "islpy" 29 | version: 2022.1.2 30 | doi: 10.5281/zenodo.6345184 31 | date-released: 2022-03-10 32 | url: "https://github.com/inducer/islpy" 33 | license: MIT 34 | -------------------------------------------------------------------------------- /doc/ref_schedule.rst: -------------------------------------------------------------------------------- 1 | Reference: Scheduling 2 | ===================== 3 | 4 | .. currentmodule:: islpy 5 | 6 | Schedule 7 | -------- 8 | 9 | .. autoclass:: schedule_node_type 10 | :members: 11 | :undoc-members: 12 | :exclude-members: @entries 13 | 14 | .. autoclass:: Schedule 15 | :members: 16 | 17 | Schedule Node 18 | ------------- 19 | 20 | .. autoclass:: ScheduleNode 21 | :members: 22 | 23 | ScheduleConstraints 24 | ------------------- 25 | 26 | .. autoclass:: ScheduleConstraints 27 | :members: 28 | 29 | Canonical Names for Internal Module 30 | ----------------------------------- 31 | 32 | .. :: 33 | 34 | This should switch to using ``:canonical:`` once Sphinx 4.0 is released. 35 | 36 | .. currentmodule:: islpy._isl 37 | 38 | .. class:: schedule_node_type 39 | 40 | See :class:`islpy.schedule_node_type`. 41 | 42 | .. class:: Schedule 43 | 44 | See :class:`islpy.Schedule`. 45 | 46 | .. class:: ScheduleNode 47 | 48 | See :class:`islpy.ScheduleNode`. 49 | 50 | .. class:: ScheduleConstraints 51 | 52 | See :class:`islpy.ScheduleConstraints`. 53 | -------------------------------------------------------------------------------- /src/wrapper/wrap_isl_part3.cpp: -------------------------------------------------------------------------------- 1 | #include "wrap_isl.hpp" 2 | 3 | namespace isl 4 | { 5 | #include "gen-wrap-part3.inc" 6 | } 7 | 8 | void islpy_expose_part3(py::module_ &m) 9 | { 10 | MAKE_WRAP(qpolynomial, QPolynomial); 11 | MAKE_WRAP(pw_qpolynomial, PwQPolynomial); 12 | MAKE_WRAP(qpolynomial_fold, QPolynomialFold); 13 | MAKE_WRAP(pw_qpolynomial_fold, PwQPolynomialFold); 14 | MAKE_WRAP(union_pw_qpolynomial_fold, UnionPwQPolynomialFold); 15 | MAKE_WRAP(union_pw_qpolynomial, UnionPwQPolynomial); 16 | 17 | MAKE_WRAP(term, Term); 18 | 19 | MAKE_WRAP(schedule, Schedule); 20 | MAKE_WRAP(schedule_constraints, ScheduleConstraints); 21 | MAKE_WRAP(schedule_node, ScheduleNode); 22 | 23 | MAKE_WRAP(access_info, AccessInfo); 24 | MAKE_WRAP(flow, Flow); 25 | MAKE_WRAP(restriction, Restriction); 26 | MAKE_WRAP(union_access_info, UnionAccessInfo); 27 | MAKE_WRAP(union_flow, UnionFlow); 28 | 29 | MAKE_WRAP(ast_expr, AstExpr); 30 | MAKE_WRAP(ast_node, AstNode); 31 | MAKE_WRAP(ast_build, AstBuild); 32 | MAKE_WRAP(ast_print_options, AstPrintOptions); 33 | 34 | #include "gen-expose-part3.inc" 35 | } 36 | -------------------------------------------------------------------------------- /doc/ref_flow.rst: -------------------------------------------------------------------------------- 1 | Dataflow 2 | ^^^^^^^^ 3 | 4 | .. currentmodule:: islpy 5 | 6 | Access Info 7 | ----------- 8 | 9 | .. autoclass:: AccessInfo 10 | :members: 11 | 12 | Union Access Info 13 | ----------------- 14 | 15 | .. autoclass:: UnionAccessInfo 16 | :members: 17 | 18 | Restriction 19 | ----------- 20 | 21 | .. autoclass:: Restriction 22 | :members: 23 | 24 | Flow 25 | ---- 26 | 27 | .. autoclass:: Flow 28 | :members: 29 | 30 | Union Flow 31 | ---------- 32 | 33 | .. autoclass:: UnionFlow 34 | :members: 35 | 36 | Canonical Names for Internal Module 37 | ----------------------------------- 38 | 39 | .. :: 40 | 41 | This should switch to using ``:canonical:`` once Sphinx 4.0 is released. 42 | 43 | .. currentmodule:: islpy._isl 44 | 45 | .. class:: AccessInfo 46 | 47 | See :class:`islpy.AccessInfo`. 48 | 49 | .. class:: UnionAccessInfo 50 | 51 | See :class:`islpy.UnionAccessInfo`. 52 | 53 | .. class:: Restriction 54 | 55 | See :class:`islpy.Restriction`. 56 | 57 | .. class:: Flow 58 | 59 | See :class:`islpy.Flow`. 60 | 61 | .. class:: UnionFlow 62 | 63 | See :class:`islpy.UnionFlow`. 64 | -------------------------------------------------------------------------------- /src/wrapper/wrap_helpers.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PYCUDA_WRAP_HELPERS_HEADER_SEEN 2 | #define PYCUDA_WRAP_HELPERS_HEADER_SEEN 3 | 4 | #include 5 | 6 | namespace py = nanobind; 7 | 8 | 9 | #define PYTHON_ERROR(TYPE, REASON) \ 10 | { \ 11 | PyErr_SetString(PyExc_##TYPE, REASON); \ 12 | throw py::python_error(); \ 13 | } 14 | 15 | #define ENUM_VALUE(PREFIX, NAME) \ 16 | value(#NAME, PREFIX##NAME) 17 | 18 | #define DEF_SIMPLE_METHOD(NAME) \ 19 | def(#NAME, &cls::NAME) 20 | 21 | #define DEF_SIMPLE_METHOD_WITH_ARGS(NAME, ARGS) \ 22 | def(#NAME, &cls::NAME, py::args ARGS) 23 | 24 | #define DEF_SIMPLE_FUNCTION(NAME) \ 25 | py::def(#NAME, &NAME) 26 | 27 | #define DEF_SIMPLE_FUNCTION_WITH_ARGS(NAME, ARGS) \ 28 | py::def(#NAME, &NAME, py::args ARGS) 29 | 30 | #define DEF_SIMPLE_RO_MEMBER(NAME) \ 31 | def_readonly(#NAME, &cls::NAME) 32 | 33 | #define DEF_SIMPLE_RW_MEMBER(NAME) \ 34 | def_readwrite(#NAME, &cls::NAME) 35 | 36 | namespace 37 | { 38 | template 39 | inline py::object handle_from_new_ptr(T *ptr) 40 | { 41 | return py::cast(ptr, py::rv_policy::take_ownership); 42 | } 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/wrapper/wrap_isl_part2.cpp: -------------------------------------------------------------------------------- 1 | #include "wrap_isl.hpp" 2 | 3 | namespace isl 4 | { 5 | #include "gen-wrap-part2.inc" 6 | } 7 | 8 | void islpy_expose_part2(py::module_ &m) 9 | { 10 | MAKE_WRAP(basic_set, BasicSet); 11 | wrap_basic_set.def("__hash__", [](isl::basic_set const &self) { 12 | isl::set set_self(self); 13 | return isl_set_get_hash(set_self.m_data); 14 | }); 15 | // used in align_dims 16 | wrap_basic_set.def("is_params", [](isl::basic_set const &self) { 17 | isl::set set_self(self); 18 | return bool(isl_set_is_params(set_self.m_data)); 19 | }); 20 | 21 | MAKE_WRAP(basic_map, BasicMap); 22 | wrap_basic_map.def("__hash__", [](isl::basic_map const &self) { 23 | isl::map map_self(self); 24 | return isl_map_get_hash(map_self.m_data); 25 | }); 26 | 27 | MAKE_WRAP(set, Set); 28 | MAKE_INIT_CONVERTIBLE(basic_set, set); 29 | 30 | MAKE_WRAP(map, Map); 31 | MAKE_INIT_CONVERTIBLE(basic_map, map); 32 | MAKE_TO_METHOD(basic_map, map); 33 | 34 | MAKE_WRAP(union_set, UnionSet); 35 | MAKE_INIT_CONVERTIBLE(set, union_set); 36 | 37 | MAKE_WRAP(union_map, UnionMap); 38 | MAKE_INIT_CONVERTIBLE(map, union_map); 39 | 40 | MAKE_WRAP(point, Point); 41 | 42 | MAKE_WRAP(vertex, Vertex); 43 | 44 | MAKE_WRAP(cell, Cell); 45 | 46 | MAKE_WRAP(vertices, Vertices); 47 | MAKE_WRAP(stride_info, StrideInfo); 48 | 49 | #include "gen-expose-part2.inc" 50 | } 51 | -------------------------------------------------------------------------------- /doc/ref_ast.rst: -------------------------------------------------------------------------------- 1 | Reference: Abstract Syntax Trees 2 | ================================ 3 | 4 | .. currentmodule:: islpy 5 | 6 | .. versionadded:: 2014.1 7 | 8 | Symbolic Constants 9 | ------------------ 10 | 11 | .. autoclass:: ast_expr_op_type 12 | :members: 13 | :undoc-members: 14 | :exclude-members: @entries 15 | 16 | .. autoclass:: ast_expr_type 17 | :members: 18 | :undoc-members: 19 | :exclude-members: @entries 20 | 21 | .. autoclass:: ast_node_type 22 | :members: 23 | :undoc-members: 24 | :exclude-members: @entries 25 | 26 | .. autoclass:: ast_loop_type 27 | :members: 28 | :undoc-members: 29 | :exclude-members: @entries 30 | 31 | AST Expression 32 | -------------- 33 | 34 | .. autoclass:: AstExpr 35 | :members: 36 | 37 | AST Node 38 | -------- 39 | 40 | .. autoclass:: AstNode 41 | :members: 42 | 43 | AST Build 44 | --------- 45 | 46 | .. autoclass:: AstBuild 47 | :members: 48 | 49 | AST Print Options 50 | ----------------- 51 | 52 | .. autoclass:: AstPrintOptions 53 | :members: 54 | 55 | Canonical Names for Internal Module 56 | ----------------------------------- 57 | 58 | .. :: 59 | 60 | This should switch to using ``:canonical:`` once Sphinx 4.0 is released. 61 | 62 | .. currentmodule:: islpy._isl 63 | 64 | .. class:: ast_expr_op_type 65 | 66 | See :class:`islpy.ast_expr_op_type`. 67 | 68 | .. class:: ast_expr_type 69 | 70 | See :class:`islpy.ast_expr_type`. 71 | 72 | .. class:: ast_node_type 73 | 74 | See :class:`islpy.ast_node_type`. 75 | 76 | .. class:: ast_loop_type 77 | 78 | See :class:`islpy.ast_loop_type`. 79 | 80 | .. class:: AstExpr 81 | 82 | See :class:`islpy.AstExpr`. 83 | 84 | .. class:: AstNode 85 | 86 | See :class:`islpy.AstNode`. 87 | 88 | .. class:: AstBuild 89 | 90 | See :class:`islpy.AstBuild`. 91 | 92 | .. class:: AstPrintOptions 93 | 94 | See :class:`islpy.AstPrintOptions`. 95 | -------------------------------------------------------------------------------- /doc/ref_expr.rst: -------------------------------------------------------------------------------- 1 | Reference: Expression-like Objects 2 | ================================== 3 | 4 | .. currentmodule:: islpy 5 | 6 | Quasi-Affine Expressions 7 | ^^^^^^^^^^^^^^^^^^^^^^^^ 8 | 9 | Quasi-Affine Expression 10 | ----------------------- 11 | 12 | .. autoclass:: Aff 13 | :members: 14 | 15 | Piecewise Quasi-Affine Expression 16 | --------------------------------- 17 | 18 | .. autoclass:: PwAff 19 | :members: 20 | 21 | Union of Piecewise Quasi-Affine Expressions 22 | ------------------------------------------- 23 | 24 | .. autoclass:: UnionPwAff 25 | :members: 26 | 27 | Multiple Union of Piecewise Quasi-Affine Expressions 28 | ---------------------------------------------------- 29 | 30 | .. autoclass:: MultiUnionPwAff 31 | :members: 32 | 33 | 34 | Multiple Affine Expressions 35 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 36 | 37 | Multiple Affine Expression 38 | -------------------------- 39 | 40 | .. autoclass:: MultiAff 41 | :members: 42 | 43 | Piecewise Multiple Affine Expression 44 | ------------------------------------ 45 | 46 | .. autoclass:: PwMultiAff 47 | :members: 48 | 49 | Multiple Piecewise Affine Expression 50 | ------------------------------------ 51 | 52 | .. autoclass:: MultiPwAff 53 | :members: 54 | 55 | Union of Piecewise Multiple Affine Expressions 56 | ---------------------------------------------- 57 | 58 | .. autoclass:: UnionPwMultiAff 59 | :members: 60 | 61 | Quasipolynomials 62 | ^^^^^^^^^^^^^^^^ 63 | 64 | Term 65 | ---- 66 | 67 | .. autoclass:: Term 68 | :members: 69 | 70 | QPolynomial 71 | ----------- 72 | 73 | .. autoclass:: QPolynomial 74 | :members: 75 | 76 | PwQPolynomial 77 | ------------- 78 | 79 | .. autoclass:: PwQPolynomial 80 | :members: 81 | 82 | UnionPwQPolynomial 83 | ------------------ 84 | 85 | .. autoclass:: UnionPwQPolynomial 86 | :members: 87 | 88 | QPolynomialFold 89 | --------------- 90 | 91 | .. autoclass:: QPolynomialFold 92 | :members: 93 | 94 | PwQPolynomial 95 | ------------- 96 | 97 | .. autoclass:: PwQPolynomialFold 98 | :members: 99 | 100 | UnionPwQPolynomialFold 101 | ---------------------- 102 | 103 | .. autoclass:: UnionPwQPolynomialFold 104 | :members: 105 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | Python 3: 2 | script: | 3 | EXTRA_INSTALL="numpy" 4 | curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh 5 | . ./build-and-test-py-project.sh 6 | tags: 7 | - python3 8 | except: 9 | - tags 10 | artifacts: 11 | reports: 12 | junit: test/pytest.xml 13 | 14 | Examples: 15 | script: | 16 | EXTRA_INSTALL="matplotlib numpy" 17 | curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/ci-support.sh 18 | . ./ci-support.sh 19 | build_py_project_in_venv 20 | run_examples 21 | tags: 22 | - python3 23 | except: 24 | - tags 25 | 26 | Python 3 without small-integer opt: 27 | script: | 28 | curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh 29 | export PROJECT_INSTALL_FLAGS="--config-settings=cmake.define.USE_IMATH_SIO=OFF" 30 | . ./build-and-test-py-project.sh 31 | tags: 32 | - python3 33 | except: 34 | - tags 35 | artifacts: 36 | reports: 37 | junit: test/pytest.xml 38 | 39 | Python 3 + Barvinok: 40 | script: | 41 | git clean -fdx 42 | python3 -m venv .env 43 | source .env/bin/activate 44 | python -m ensurepip 45 | pip install pcpp numpy pytest 46 | ./build-with-barvinok.sh "$HOME/barvinok-build" 47 | (cd test; LD_LIBRARY_PATH="$HOME/barvinok-build/lib" python -m pytest --tb=native -rxsw) 48 | 49 | tags: 50 | - python3 51 | except: 52 | - tags 53 | artifacts: 54 | reports: 55 | junit: test/pytest.xml 56 | 57 | PyPy3: 58 | script: 59 | - export PY_EXE=pypy3 60 | - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh 61 | - ". ./build-and-test-py-project.sh" 62 | allow_failure: true 63 | tags: 64 | - pypy 65 | except: 66 | - tags 67 | artifacts: 68 | reports: 69 | junit: test/pytest.xml 70 | 71 | Documentation: 72 | script: | 73 | curl -L -O -k https://tiker.net/ci-support-v0 74 | . ci-support-v0 75 | build_py_project_in_venv 76 | 77 | build_docs 78 | maybe_upload_docs 79 | 80 | tags: 81 | - python3 82 | only: 83 | - main 84 | 85 | Ruff: 86 | script: 87 | - pipx install ruff 88 | - ruff check 89 | tags: 90 | - docker-runner 91 | except: 92 | - tags 93 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | islpy: Polyhedral Analysis from Python 2 | ====================================== 3 | 4 | .. image:: https://gitlab.tiker.net/inducer/islpy/badges/main/pipeline.svg 5 | :alt: Gitlab Build Status 6 | :target: https://gitlab.tiker.net/inducer/islpy/commits/main 7 | .. image:: https://github.com/inducer/islpy/actions/workflows/ci.yml/badge.svg 8 | :alt: Github Build Status 9 | :target: https://github.com/inducer/islpy/actions/workflows/ci.yml 10 | .. image:: https://badge.fury.io/py/islpy.svg 11 | :alt: Python Package Index Release Page 12 | :target: https://pypi.org/project/islpy/ 13 | .. image:: https://zenodo.org/badge/2021524.svg 14 | :alt: Zenodo DOI for latest release 15 | :target: https://zenodo.org/badge/latestdoi/2021524 16 | 17 | islpy is a Python wrapper around Sven Verdoolaege's `isl 18 | `__, a library for manipulating sets and 19 | relations of integer points bounded by linear constraints. 20 | 21 | Supported operations on sets include 22 | 23 | * intersection, union, set difference, 24 | * emptiness check, 25 | * convex hull, 26 | * (integer) affine hull, 27 | * integer projection, 28 | * computing the lexicographic minimum using parametric integer programming, 29 | * coalescing, and 30 | * parametric vertex enumeration. 31 | 32 | It also includes an ILP solver based on generalized basis reduction, transitive 33 | closures on maps (which may encode infinite graphs), dependence analysis and 34 | bounds on piecewise step-polynomials. 35 | 36 | Islpy comes with comprehensive `documentation `__. 37 | 38 | *Requirements:* islpy needs a C++ compiler to build. It can optionally make use 39 | of GMP for support of large integers. 40 | 41 | One important thing to know about islpy is that it exposes every function in isl 42 | that is visible in the headers, not just what isl's authors consider its 43 | documented, public API (marked by ``__isl_export``). These (technically) 44 | undocumented functions are marked in the islpy documentation. Many of them are useful 45 | and essential for certain operations, but isl's API stability guarantees do not 46 | apply to them. Use them at your own risk. 47 | 48 | Islpy can optionally be built with support for `barvinok `__, 49 | a library for counting the number of integer points in parametric and non-parametric 50 | polytopes. Notably, unlike isl, barvinok is GPL-licensed, so doing so changes 51 | islpy's effective license as well. In addition to islpy's `regular PyPI source 52 | and binary wheel downloads `__, `Cambridge Yang 53 | `__ has made available a `package with wheels that 54 | include Barvinok `__. 55 | 56 | -------------------------------------------------------------------------------- /examples/demo.py: -------------------------------------------------------------------------------- 1 | import islpy as isl 2 | 3 | 4 | space = isl.Space.create_from_names(isl.DEFAULT_CONTEXT, set=["x", "y"]) 5 | 6 | bset = (isl.BasicSet.universe(space) 7 | .add_constraint(isl.Constraint.ineq_from_names(space, {1: -1, "x": 1})) 8 | .add_constraint(isl.Constraint.ineq_from_names(space, {1: 5, "x": -1})) 9 | .add_constraint(isl.Constraint.ineq_from_names(space, {1: -1, "y": 1})) 10 | .add_constraint(isl.Constraint.ineq_from_names(space, {1: 5, "y": -1}))) 11 | print("set 1 %s:" % bset) 12 | 13 | bset2 = isl.BasicSet("{[x, y] : x >= 0 and x < 5 and y >= 0 and y < x+4 }") 14 | print("set 2: %s" % bset2) 15 | 16 | union_ch = (bset.to_set() | bset2).convex_hull() 17 | print("convex hull of union: %s" % union_ch) 18 | # ENDEXAMPLE 19 | 20 | import matplotlib.pyplot as pt 21 | 22 | 23 | def plot_basic_set(bset: isl.BasicSet, *args, **kwargs): 24 | # This is a total hack. But it works for what it needs to do. :) 25 | 26 | plot_vert = kwargs.pop("plot_vert", False) 27 | 28 | vertices: list[isl.Vertex] = [] 29 | bset.compute_vertices().foreach_vertex(vertices.append) 30 | 31 | vertex_pts: list[list[int]] = [] 32 | 33 | for v in vertices: 34 | points: list[isl.Point] = [] 35 | myset = isl.BasicSet.from_multi_aff(v.get_expr()) 36 | myset.to_set().foreach_point(points.append) 37 | point, = points 38 | vertex_pts.append([ 39 | point.get_coordinate_val(isl.dim_type.set, i).to_python() 40 | for i in range(2)]) 41 | 42 | import numpy as np 43 | vertex_pts_ary = np.array(vertex_pts) 44 | 45 | center = np.average(vertex_pts_ary, axis=0) 46 | 47 | from math import atan2 48 | vertex_pts = np.array( 49 | sorted(vertex_pts, key=lambda x: atan2(x[1]-center[1], x[0]-center[0]))) 50 | 51 | if plot_vert: 52 | pt.plot(vertex_pts[:, 0], vertex_pts[:, 1], "o") 53 | 54 | import matplotlib.patches as mpatches 55 | import matplotlib.path as mpath 56 | 57 | Path = mpath.Path # noqa 58 | 59 | codes = [Path.LINETO] * len(vertex_pts) 60 | codes[0] = Path.MOVETO 61 | 62 | pathdata = [ 63 | (code, tuple(coord)) for code, coord in zip(codes, vertex_pts, strict=True)] 64 | pathdata.append((Path.CLOSEPOLY, (0, 0))) 65 | 66 | codes, verts = zip(*pathdata, strict=True) 67 | path = mpath.Path(verts, codes) 68 | patch = mpatches.PathPatch(path, **kwargs) 69 | pt.gca().add_patch(patch) 70 | 71 | 72 | plot_basic_set(bset, facecolor="red", edgecolor="black", alpha=0.3) 73 | plot_basic_set(bset2, facecolor="green", edgecolor="black", alpha=0.2) 74 | pt.grid() 75 | pt.xlim([-1, 6]) 76 | pt.ylim([-1, 8]) 77 | # pt.show() 78 | pt.savefig("before-union.png", dpi=50) 79 | 80 | plot_basic_set(union_ch, facecolor="blue", edgecolor="yellow", 81 | alpha=0.5, plot_vert=True) 82 | pt.savefig("after-union.png", dpi=50) 83 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | import re 2 | from urllib.request import urlopen 3 | 4 | 5 | _conf_url = "https://tiker.net/sphinxconfig-v0.py" 6 | with urlopen(_conf_url) as _inf: 7 | exec(compile(_inf.read(), _conf_url, "exec"), globals()) 8 | 9 | extensions.remove("sphinx.ext.linkcode") # noqa: F821 10 | 11 | copyright = "2011-21, Andreas Kloeckner" 12 | 13 | ver_dic = {} 14 | with open("../islpy/version.py") as vfile: 15 | exec(compile(vfile.read(), "../islpy/version.py", "exec"), ver_dic) 16 | 17 | version = ".".join(str(x) for x in ver_dic["VERSION"]) 18 | # The full version, including alpha/beta/rc tags. 19 | release = ver_dic["VERSION_TEXT"] 20 | 21 | intersphinx_mapping = { 22 | "python": ("https://docs.python.org/3/", None), 23 | } 24 | 25 | 26 | def autodoc_process_docstring(app, what, name, obj, options, lines: list[str]): 27 | arg_list_re = re.compile(r"^([a-zA-Z0-9_]+)\((.*?)\)") 28 | 29 | from inspect import isclass, isroutine 30 | UNDERSCORE_WHITELIST = ["__len__", "__hash__", "__eq__", "__ne__"] # noqa: N806 31 | if isclass(obj) and obj.__name__[0].isupper(): 32 | methods = [nm for nm in dir(obj) 33 | if isroutine(getattr(obj, nm)) 34 | and (not nm.startswith("_") or nm in UNDERSCORE_WHITELIST)] 35 | 36 | def gen_method_string(meth_name: str): 37 | try: 38 | result: str = ":meth:`%s`" % meth_name 39 | meth_obj = getattr(obj, meth_name) 40 | if meth_obj.__doc__ is None: 41 | return result 42 | 43 | doc_match = arg_list_re.match(meth_obj.__doc__) 44 | if doc_match is None: 45 | # print(f"'{meth_obj.__doc__}' did not match arg list RE") 46 | return result 47 | 48 | arg_list = doc_match.group(2).split(", ") 49 | 50 | if "self" not in arg_list: 51 | result += " (static)" 52 | 53 | return result 54 | except Exception: 55 | from traceback import print_exc 56 | print_exc() 57 | raise 58 | 59 | if methods: 60 | lines[:] = [".. hlist::", " :columns: 2", ""] + [ 61 | " * "+gen_method_string(meth_name) 62 | for meth_name in methods] + lines 63 | 64 | for nm in methods: 65 | underscore_autodoc: list[str] = [] 66 | if nm in UNDERSCORE_WHITELIST: 67 | underscore_autodoc.append(".. automethod:: %s" % nm) 68 | 69 | if underscore_autodoc: 70 | lines.append("") 71 | lines.extend(underscore_autodoc) 72 | 73 | 74 | autodoc_default_options = { 75 | "undoc-members": True, 76 | } 77 | 78 | 79 | def setup(app): 80 | app.connect("autodoc-process-docstring", autodoc_process_docstring) 81 | -------------------------------------------------------------------------------- /stubgen/stubgen.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import importlib 3 | import os 4 | import sys 5 | from collections.abc import Callable 6 | from pathlib import Path 7 | from typing import TYPE_CHECKING, Any, cast 8 | 9 | from nanobind.stubgen import StubGen as StubGenBase 10 | from typing_extensions import override 11 | 12 | 13 | if TYPE_CHECKING: 14 | import enum 15 | 16 | 17 | class StubGen(StubGenBase): 18 | # can be removed once https://github.com/wjakob/nanobind/pull/1055 is merged 19 | @override 20 | def put_function(self, 21 | fn: Callable[..., Any], 22 | name: str | None = None, 23 | parent: object | None = None 24 | ): 25 | fn_module = getattr(fn, "__module__", None) 26 | 27 | if (name and fn_module 28 | and fn_module != self.module.__name__ 29 | and parent is not None): 30 | self.import_object(fn_module, name=None) 31 | rhs = f"{fn_module}.{fn.__qualname__}" 32 | if type(fn) is staticmethod: 33 | rhs = f"staticmethod({rhs})" 34 | self.write_ln(f"{name} = {rhs}\n") 35 | 36 | return 37 | 38 | super().put_function(fn, name, parent) 39 | 40 | @override 41 | def put(self, 42 | value: object, 43 | name: str | None = None, 44 | parent: object | None = None 45 | ) -> None: 46 | if name == "in_" and parent and parent.__name__ == "dim_type": 47 | # https://github.com/wjakob/nanobind/discussions/1066 48 | self.write_ln(f"{name} = {cast('enum.Enum', value).value}") 49 | 50 | super().put(value, name, parent) 51 | 52 | 53 | def main(): 54 | parser = argparse.ArgumentParser() 55 | parser.add_argument("-m", "--module", default="islpy._isl") 56 | parser.add_argument("--exec", nargs="+") 57 | parser.add_argument("--python-path", nargs="+") 58 | parser.add_argument("-o", "--output-dir", default="../islpy") 59 | args = parser.parse_args() 60 | output_path = Path(cast("str", args.output_dir)) 61 | 62 | sys.path.extend(cast("list[str]", args.python_path or [])) 63 | 64 | os.environ["ISLPY_NO_DOWNCAST_DEPRECATION"] = "1" 65 | mod = importlib.import_module(cast("str", args.module)) 66 | for fname in cast("list[str]", args.exec or []): 67 | execdict = {"__name__": "islpy._monkeypatch"} 68 | with open(fname) as inf: 69 | exec(compile(inf.read(), fname, "exec"), execdict) 70 | 71 | sg = StubGen( 72 | module=mod, 73 | quiet=True, 74 | recursive=False, 75 | include_docstrings=False, 76 | ) 77 | sg.put(mod) 78 | prefix_lines = "\n".join([ 79 | "from typing_extensions import Self", 80 | "from collections.abc import Callable", 81 | ]) 82 | with open(output_path / "_isl.pyi", "w") as outf: 83 | outf.write(f"{prefix_lines}\n{sg.get()}") 84 | 85 | 86 | if __name__ == "__main__": 87 | main() 88 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to islpy's documentation! 2 | ================================= 3 | 4 | islpy is a Python wrapper around Sven Verdoolaege's `isl 5 | `_, a library for manipulating sets and 6 | relations of integer points bounded by linear constraints. 7 | 8 | Supported operations on sets include 9 | 10 | * intersection, union, set difference, 11 | * emptiness check, 12 | * convex hull, 13 | * (integer) affine hull, 14 | * integer projection, 15 | * computing the lexicographic minimum using parametric integer programming, 16 | * coalescing, and 17 | * parametric vertex enumeration. 18 | 19 | It also includes an ILP solver based on generalized basis reduction, transitive 20 | closures on maps (which may encode infinite graphs), dependence analysis and 21 | bounds on piecewise step-polynomials. 22 | 23 | Now you obviously want to watch the library do something (at least mildly) 24 | cool? Well, sit back and watch: 25 | 26 | .. literalinclude:: ../examples/demo.py 27 | :end-before: ENDEXAMPLE 28 | 29 | This prints the following:: 30 | 31 | set 1: { [x, y] : x >= 1 and x <= 5 and y >= 1 and y <= 5 } 32 | set 2: { [x, y] : x >= 0 and x <= 4 and y >= 0 and y <= 3 + x } 33 | union: { [x, y] : x >= 0 and y >= 0 and x <= 5 and y <= 3 + 2x and y >= -4 + x and y <= 15 - 2x and 3y <= 13 + 2x } 34 | 35 | With some hacky plotting code (not shown), you can actually see what this 36 | example just did. We gave it the two polyhedra on the left, asked it to compute 37 | the union, and computed the convex hull: 38 | 39 | +-------------------------------------+-------------------------------------+ 40 | | .. image:: images/before-union.png | .. image:: images/after-union.png | 41 | +-------------------------------------+-------------------------------------+ 42 | 43 | See :download:`example/demo.py <../examples/demo.py>` to see the full example, 44 | including the less-than-perfect plotting code. :) 45 | 46 | Note that far better plotting of isl(py) sets is available by installing Tobias 47 | Grosser's `islplot package `_. 48 | 49 | Overview 50 | -------- 51 | 52 | This manual will not try to teach you much about the isl itself, it simply 53 | lists, in a reference fashion, all the entrypoints available in :mod:`islpy`. 54 | To get information on how to use the isl, see the real `isl manual 55 | `_. The `manual 56 | `_ for the `barvinok 57 | package `_ is also quite helpful to get 58 | an idea. 59 | 60 | .. toctree:: 61 | :maxdepth: 2 62 | 63 | misc 64 | reference 65 | ref_fundamental 66 | ref_expr 67 | ref_set 68 | ref_geo 69 | ref_ast 70 | ref_flow 71 | ref_schedule 72 | ref_containers 73 | 🚀 Github 74 | 💾 Download Releases 75 | 76 | Indices and tables 77 | ------------------ 78 | 79 | * :ref:`genindex` 80 | * :ref:`modindex` 81 | * :ref:`search` 82 | 83 | -------------------------------------------------------------------------------- /doc/ref_containers.rst: -------------------------------------------------------------------------------- 1 | Reference: Containers 2 | ===================== 3 | 4 | .. currentmodule:: islpy 5 | 6 | Lists 7 | ^^^^^ 8 | 9 | .. autoclass:: IdList 10 | :members: 11 | 12 | .. autoclass:: ValList 13 | :members: 14 | 15 | .. autoclass:: BasicSetList 16 | :members: 17 | 18 | .. autoclass:: AffList 19 | :members: 20 | 21 | .. autoclass:: PwAffList 22 | :members: 23 | 24 | .. autoclass:: PwMultiAffList 25 | :members: 26 | 27 | .. autoclass:: UnionPwAffList 28 | :members: 29 | 30 | .. autoclass:: UnionPwMultiAffList 31 | :members: 32 | 33 | .. autoclass:: ConstraintList 34 | :members: 35 | 36 | .. autoclass:: BasicMapList 37 | :members: 38 | 39 | .. autoclass:: SetList 40 | :members: 41 | 42 | .. autoclass:: MapList 43 | :members: 44 | 45 | .. autoclass:: UnionSetList 46 | :members: 47 | 48 | .. autoclass:: UnionMapList 49 | :members: 50 | 51 | .. autoclass:: AstExprList 52 | :members: 53 | 54 | .. autoclass:: AstNodeList 55 | :members: 56 | 57 | .. autoclass:: QPolynomialList 58 | :members: 59 | 60 | .. autoclass:: PwQPolynomialList 61 | :members: 62 | 63 | .. autoclass:: PwQPolynomialFoldList 64 | :members: 65 | 66 | Dictionaries 67 | ^^^^^^^^^^^^ 68 | 69 | .. autoclass:: IdToAstExpr 70 | :members: 71 | 72 | Multi Types 73 | ----------- 74 | 75 | Canonical Names for Internal Module 76 | ----------------------------------- 77 | 78 | .. :: 79 | 80 | This should switch to using ``:canonical:`` once Sphinx 4.0 is released. 81 | 82 | .. currentmodule:: islpy._isl 83 | 84 | .. class:: IdList 85 | 86 | See :class:`islpy.IdList`. 87 | 88 | .. class:: ValList 89 | 90 | See :class:`islpy.ValList`. 91 | 92 | .. class:: BasicSetList 93 | 94 | See :class:`islpy.BasicSetList`. 95 | 96 | .. class:: AffList 97 | 98 | See :class:`islpy.AffList`. 99 | 100 | .. class:: PwAffList 101 | 102 | See :class:`islpy.PwAffList`. 103 | 104 | .. class:: PwMultiAffList 105 | 106 | See :class:`islpy.PwMultiAffList`. 107 | 108 | .. class:: UnionPwAffList 109 | 110 | See :class:`islpy.UnionPwAffList`. 111 | 112 | .. class:: UnionPwMultiAffList 113 | 114 | See :class:`islpy.UnionPwMultiAffList`. 115 | 116 | .. class:: ConstraintList 117 | 118 | See :class:`islpy.ConstraintList`. 119 | 120 | .. class:: BasicMapList 121 | 122 | See :class:`islpy.BasicMapList`. 123 | 124 | .. class:: SetList 125 | 126 | See :class:`islpy.SetList`. 127 | 128 | .. class:: MapList 129 | 130 | See :class:`islpy.MapList`. 131 | 132 | .. class:: UnionSetList 133 | 134 | See :class:`islpy.UnionSetList`. 135 | 136 | .. class:: UnionMapList 137 | 138 | See :class:`islpy.UnionMapList`. 139 | 140 | .. class:: AstExprList 141 | 142 | See :class:`islpy.AstExprList`. 143 | 144 | .. class:: AstNodeList 145 | 146 | See :class:`islpy.AstNodeList`. 147 | 148 | .. class:: IdToAstExpr 149 | 150 | See :class:`islpy.IdToAstExpr`. 151 | 152 | .. class:: PwQPolynomialList 153 | 154 | See :class:`islpy.PwQPolynomialList`. 155 | 156 | .. class:: PwQPolynomialFoldList 157 | 158 | See :class:`islpy.PwQPolynomialFoldList`. 159 | 160 | .. vim: sw=4 161 | -------------------------------------------------------------------------------- /.github/workflows/wheels.yml: -------------------------------------------------------------------------------- 1 | name: Build wheels 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - v* 9 | pull_request: 10 | 11 | jobs: 12 | build_wheels: 13 | name: Build wheels on ${{ matrix.os }} 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest, macos-15-intel, macos-14, ubuntu-22.04-arm] 18 | 19 | steps: 20 | - uses: actions/checkout@v6 21 | with: 22 | submodules: recursive 23 | persist-credentials: false 24 | 25 | - name: Build wheels 26 | uses: pypa/cibuildwheel@v3.3.0 27 | # (here: set these in pyproject.toml to the extent possible) 28 | # env: 29 | # CIBW_SOME_OPTION: value 30 | # CIBW_BUILD_VERBOSITY: 1 31 | # VERBOSE: 1 32 | 33 | - uses: actions/upload-artifact@v6 34 | with: 35 | name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} 36 | path: ./wheelhouse/*.whl 37 | 38 | build_sdist: 39 | name: Build source distribution 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v6 43 | with: 44 | submodules: recursive 45 | 46 | - name: Build sdist 47 | run: | 48 | pipx --version 49 | pipx run build --sdist 50 | 51 | - uses: actions/upload-artifact@v6 52 | with: 53 | name: cibw-sdist 54 | path: dist/*.tar.gz 55 | 56 | upload_pypi: 57 | needs: [build_wheels, build_sdist] 58 | runs-on: ubuntu-latest 59 | # upload to PyPI on every tag starting with 'v' 60 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') 61 | # alternatively, to publish when a GitHub Release is created, use the following rule: 62 | # if: github.event_name == 'release' && github.event.action == 'published' 63 | environment: 64 | name: pypi 65 | url: https://pypi.org/p/islpy 66 | permissions: 67 | id-token: write # IMPORTANT: mandatory for trusted publishing 68 | steps: 69 | - uses: actions/download-artifact@v7 70 | with: 71 | pattern: cibw-* 72 | path: dist 73 | merge-multiple: true 74 | 75 | - uses: pypa/gh-action-pypi-publish@v1.13.0 76 | # with: 77 | # user: __token__ 78 | # password: ${{ secrets.pypi_password }} 79 | # To test: repository_url: https://test.pypi.org/legacy/ 80 | 81 | upload_testpypi: 82 | if: startsWith(github.ref, 'refs/tags/testv') 83 | needs: [build_wheels, build_sdist] 84 | runs-on: ubuntu-latest 85 | 86 | environment: 87 | name: testpypi 88 | url: https://test.pypi.org/p/islpy 89 | 90 | permissions: 91 | id-token: write # IMPORTANT: mandatory for trusted publishing 92 | 93 | steps: 94 | - name: Download all the dists 95 | uses: actions/download-artifact@v7 96 | with: 97 | pattern: cibw-* 98 | path: dist/ 99 | merge-multiple: true 100 | - name: Publish distribution 📦 to TestPyPI 101 | uses: pypa/gh-action-pypi-publish@release/v1 102 | with: 103 | repository-url: https://test.pypi.org/legacy/ 104 | -------------------------------------------------------------------------------- /doc/ref_fundamental.rst: -------------------------------------------------------------------------------- 1 | Reference: Basic Building Blocks 2 | ================================ 3 | 4 | .. currentmodule:: islpy 5 | 6 | Context 7 | ------- 8 | 9 | .. autoclass:: Context() 10 | :members: 11 | 12 | Id 13 | -- 14 | 15 | .. autoclass:: Id 16 | :members: 17 | 18 | .. autoclass:: MultiId 19 | :members: 20 | 21 | Space 22 | ----- 23 | 24 | (formerly called ``Dim``. A compatibility alias is in place.) 25 | 26 | .. autoclass:: Space 27 | :members: 28 | 29 | Local Space 30 | ----------- 31 | 32 | .. autoclass:: LocalSpace 33 | :members: 34 | 35 | Constraints 36 | ----------- 37 | 38 | .. autoclass:: Constraint 39 | :members: 40 | 41 | Value 42 | ----- 43 | 44 | .. autoclass:: Val 45 | :members: 46 | 47 | Multi-Value 48 | ----------- 49 | 50 | .. autoclass:: MultiVal 51 | :members: 52 | 53 | Vector 54 | ------ 55 | 56 | .. autoclass:: Vec 57 | :members: 58 | 59 | Matrix 60 | ------ 61 | 62 | .. autoclass:: Mat 63 | :members: 64 | 65 | Canonical Names for Internal Module 66 | ----------------------------------- 67 | 68 | .. :: 69 | 70 | This should switch to using ``:canonical:`` once Sphinx 4.0 is released. 71 | 72 | .. currentmodule:: islpy._isl 73 | 74 | .. class:: Context 75 | 76 | See :class:`islpy.Context`. 77 | 78 | .. class:: Id 79 | 80 | See :class:`islpy.Id`. 81 | 82 | .. class:: MultiId 83 | 84 | See :class:`islpy.MultiId`. 85 | 86 | .. class:: Space 87 | 88 | See :class:`islpy.Space`. 89 | 90 | .. class:: LocalSpace 91 | 92 | See :class:`islpy.LocalSpace`. 93 | 94 | .. class:: Constraint 95 | 96 | See :class:`islpy.Constraint`. 97 | 98 | .. class:: Val 99 | 100 | See :class:`islpy.Val`. 101 | 102 | .. class:: MultiVal 103 | 104 | See :class:`islpy.MultiVal`. 105 | 106 | .. class:: Vec 107 | 108 | See :class:`islpy.Vec`. 109 | 110 | .. class:: Mat 111 | 112 | See :class:`islpy.Mat`. 113 | 114 | .. class:: BasicSet 115 | 116 | See :class:`islpy.BasicSet`. 117 | 118 | .. class:: BasicMap 119 | 120 | See :class:`islpy.BasicMap`. 121 | 122 | .. class:: Set 123 | 124 | See :class:`islpy.Set`. 125 | 126 | .. class:: Map 127 | 128 | See :class:`islpy.Map`. 129 | 130 | .. class:: UnionSet 131 | 132 | See :class:`islpy.UnionSet`. 133 | 134 | .. class:: UnionMap 135 | 136 | See :class:`islpy.UnionMap`. 137 | 138 | .. class:: Point 139 | 140 | See :class:`islpy.Point`. 141 | 142 | .. class:: Vertex 143 | 144 | See :class:`islpy.Vertex`. 145 | 146 | .. class:: Vertices 147 | 148 | See :class:`islpy.Vertices`. 149 | 150 | .. class:: StrideInfo 151 | 152 | See :class:`islpy.StrideInfo`. 153 | 154 | .. class:: Cell 155 | 156 | See :class:`islpy.Cell`. 157 | 158 | .. class:: FixedBox 159 | 160 | See :class:`islpy.FixedBox`. 161 | 162 | .. class:: Aff 163 | 164 | See :class:`islpy.Aff`. 165 | 166 | .. class:: Div 167 | 168 | See :class:`islpy.Aff` (not a typo!). 169 | 170 | .. class:: PwAff 171 | 172 | See :class:`islpy.PwAff`. 173 | 174 | .. class:: UnionPwAff 175 | 176 | See :class:`islpy.UnionPwAff`. 177 | 178 | .. class:: MultiUnionPwAff 179 | 180 | See :class:`islpy.MultiUnionPwAff`. 181 | 182 | .. class:: MultiAff 183 | 184 | See :class:`islpy.MultiAff`. 185 | 186 | .. class:: PwMultiAff 187 | 188 | See :class:`islpy.PwMultiAff`. 189 | 190 | .. class:: MultiPwAff 191 | 192 | See :class:`islpy.MultiPwAff`. 193 | 194 | .. class:: UnionPwMultiAff 195 | 196 | See :class:`islpy.UnionPwMultiAff`. 197 | 198 | .. class:: Term 199 | 200 | See :class:`islpy.Term`. 201 | 202 | .. class:: QPolynomial 203 | 204 | See :class:`islpy.QPolynomial`. 205 | 206 | .. class:: PwQPolynomial 207 | 208 | See :class:`islpy.PwQPolynomial`. 209 | 210 | .. class:: UnionPwQPolynomial 211 | 212 | See :class:`islpy.UnionPwQPolynomial`. 213 | 214 | .. class:: QPolynomialFold 215 | 216 | See :class:`islpy.QPolynomialFold`. 217 | 218 | .. class:: PwQPolynomialFold 219 | 220 | See :class:`islpy.PwQPolynomialFold`. 221 | 222 | .. class:: UnionPwQPolynomialFold 223 | 224 | See :class:`islpy.UnionPwQPolynomialFold`. 225 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "scikit_build_core.build" 3 | requires = [ 4 | "scikit-build-core >=0.9.3", 5 | "nanobind >=2.7", 6 | "pcpp", 7 | 8 | # stubgen uses @override :/ 9 | "typing_extensions>=4.5", 10 | ] 11 | 12 | [project] 13 | name = "islpy" 14 | version = "2025.2.5" 15 | description = "Wrapper around isl, an integer set library" 16 | readme = "README.rst" 17 | license = "MIT" 18 | authors = [ 19 | { name = "Andreas Kloeckner", email = "inform@tiker.net" }, 20 | ] 21 | requires-python = "~=3.10" 22 | 23 | classifiers = [ 24 | "Development Status :: 4 - Beta", 25 | "Intended Audience :: Developers", 26 | "Intended Audience :: Other Audience", 27 | "Intended Audience :: Science/Research", 28 | "Programming Language :: C++", 29 | "Programming Language :: Python", 30 | "Programming Language :: Python :: 3", 31 | "Topic :: Scientific/Engineering", 32 | "Topic :: Scientific/Engineering :: Mathematics", 33 | "Topic :: Scientific/Engineering :: Physics", 34 | "Topic :: Scientific/Engineering :: Visualization", 35 | "Topic :: Software Development :: Libraries", 36 | ] 37 | 38 | [project.urls] 39 | Documentation = "https://documen.tician.de/islpy" 40 | Repository = "https://github.com/inducer/islpy" 41 | 42 | 43 | [dependency-groups] 44 | dev = [ 45 | "pytest>=2", 46 | ] 47 | 48 | [tool.inducer-ci-support] 49 | disable-editable-pip-install = true 50 | 51 | [tool.scikit-build] 52 | sdist.exclude = [ 53 | ".github", 54 | "run-*.sh", 55 | "isl/test_inputs", 56 | "isl/testsets", 57 | "isl/m4", 58 | ".basedpyright", 59 | "isl/include/isl/typed_cpp.h", 60 | "isl/include/isl/cpp.h", 61 | "isl/include/isl/cpp-checked.h", 62 | "isl/imath/tests", 63 | "isl/interface/isl.py.core", 64 | ] 65 | sdist.include = [ 66 | "isl-supplementary" 67 | ] 68 | 69 | # FIXME: Comment out before committing 70 | # Use with --no-build-isolation for fast development builds 71 | # build-dir = "build/{wheel_tag}" 72 | 73 | [tool.ruff] 74 | preview = true 75 | exclude = [ 76 | "isl", 77 | "aksetup_helper.py" 78 | ] 79 | target-version = "py310" 80 | 81 | 82 | [tool.ruff.lint] 83 | extend-select = [ 84 | "B", # flake8-bugbear 85 | "C", # flake8-comprehensions 86 | "E", # pycodestyle 87 | "F", # pyflakes 88 | "G", # flake8-logging-format 89 | "I", # flake8-isort 90 | "N", # pep8-naming 91 | "NPY", # numpy 92 | "Q", # flake8-quotes 93 | "UP", # pyupgrade 94 | "RUF", # ruff 95 | "W", # pycodestyle 96 | "TC", 97 | ] 98 | extend-ignore = [ 99 | "C90", # McCabe complexity 100 | "E221", # multiple spaces before operator 101 | "E226", # missing whitespace around arithmetic operator 102 | "E402", # module-level import not at top of file 103 | "UP031", # use f-strings instead of % 104 | "UP032", # use f-strings instead of .format 105 | ] 106 | 107 | [tool.ruff.lint.flake8-quotes] 108 | docstring-quotes = "double" 109 | inline-quotes = "double" 110 | multiline-quotes = "double" 111 | 112 | [tool.ruff.lint.isort] 113 | combine-as-imports = true 114 | known-local-folder = [ 115 | "islpy", 116 | ] 117 | lines-after-imports = 2 118 | 119 | [tool.ruff.lint.per-file-ignores] 120 | "islpy/*.pyi" = [ 121 | "N801", "N802", "E501", "I001", "F401", "E202", "E203", "Q000", 122 | "RUF012" 123 | ] 124 | 125 | [tool.cibuildwheel] 126 | # i686 does not have enough memory for LTO to complete 127 | # 3.14 on musl has a hard-to-debug test crash 128 | skip = ["*_i686", 'cp31?t-*', "cp314-musllinux_x86_64"] 129 | 130 | test-requires = "pytest" 131 | test-command = "pytest {project}/test" 132 | 133 | [tool.cibuildwheel.macos.environment] 134 | # Needed for full C++17 support 135 | MACOSX_DEPLOYMENT_TARGET = "10.14" 136 | 137 | 138 | [tool.basedpyright] 139 | reportImplicitStringConcatenation = "none" 140 | reportUnnecessaryIsInstance = "none" 141 | reportUnusedCallResult = "none" 142 | reportExplicitAny = "none" 143 | reportUnreachable = "none" 144 | 145 | # This reports even cycles that are qualified by 'if TYPE_CHECKING'. Not what 146 | # we care about at this moment. 147 | # https://github.com/microsoft/pyright/issues/746 148 | reportImportCycles = "none" 149 | pythonVersion = "3.10" 150 | pythonPlatform = "All" 151 | 152 | [[tool.basedpyright.executionEnvironments]] 153 | root = "islpy/_monkeypatch.py" 154 | reportUnknownArgumentType = "hint" 155 | reportAttributeAccessIssue = "none" 156 | reportPrivateUsage = "none" 157 | 158 | [[tool.basedpyright.executionEnvironments]] 159 | root = "islpy/_isl.pyi" 160 | reportUnannotatedClassAttribute = "none" 161 | reportImplicitOverride = "none" 162 | 163 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python $(shell which sphinx-build) 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | htmlhelp: 65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 66 | @echo 67 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 68 | ".hhp project file in $(BUILDDIR)/htmlhelp." 69 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/islpy.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/islpy.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/islpy" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/islpy" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /doc/reference.rst: -------------------------------------------------------------------------------- 1 | Reference guide: Overview 2 | ========================= 3 | 4 | .. module:: islpy 5 | .. moduleauthor:: Andreas Kloeckner 6 | 7 | .. _gen-remarks: 8 | 9 | General Remarks 10 | ^^^^^^^^^^^^^^^ 11 | 12 | Creation via Static Methods 13 | --------------------------- 14 | 15 | To map more directly to the isl's C interface, object creation in :mod:`islpy` 16 | is done through static methods instead of through constructors. These are 17 | marked '(static)' in each class's overview section. 18 | 19 | Documented vs. Non-documented Functionality 20 | ------------------------------------------- 21 | 22 | Since :mod:`islpy` is automatically generated from the isl C headers, some of 23 | the functionality it exposes might be undocumented. Undocumented functionality 24 | might change or vanish without notice. 'Documented' functionality is defined as 25 | whatever is mentioned in the `isl manual 26 | `_. :mod:`islpy` will let you call 27 | undocumented functions, but you are doing so at your own risk. 28 | 29 | .. _auto-invalidation: 30 | 31 | Invalidation of Arguments 32 | ------------------------- 33 | 34 | You may notice that a few methods below say '(becomes invalid)'. This has to do 35 | with an idiosyncrasy in isl's interface that was retained at the Python level 36 | for efficiency. Such arguments will be deleted (by isl) upon entry to the 37 | called function. If you would like to retain access to that object, simply 38 | append a `.copy()` to that argument. (Note that you will notice if an object 39 | got deleted for you accidentally, as the next operation on it will simply fail 40 | with an exception.) 41 | 42 | .. _automatic-casts: 43 | 44 | Automatic Casts 45 | --------------- 46 | 47 | :mod:`islpy` will automatically perform the following upward casts in argument 48 | lists: 49 | 50 | ==================== ========================== 51 | Called with Argument Type 52 | ==================== ========================== 53 | :class:`BasicSet` :class:`Set` 54 | :class:`Set` :class:`UnionSet` 55 | :class:`BasicMap` :class:`Map` 56 | :class:`Map` :class:`UnionMap` 57 | :class:`Aff` :class:`PwAff` 58 | :class:`PwAff` :class:`UnionPwAff` 59 | :class:`MultiAff` :class:`PwMultiAff` 60 | :class:`PwMultiAff` :class:`UnionPwMultiAff` 61 | :class:`Space` :class:`LocalSpace` 62 | ==================== ========================== 63 | 64 | as well as casts contained in the transitive closure of this 'casting graph'. 65 | 66 | Version Info 67 | ------------ 68 | 69 | .. data:: version 70 | 71 | .. function:: isl_version 72 | 73 | Error Reporting 74 | --------------- 75 | 76 | .. exception:: Error 77 | 78 | Convenience 79 | ^^^^^^^^^^^ 80 | 81 | .. autofunction:: make_zero_and_vars 82 | 83 | .. autofunction:: affs_from_space 84 | 85 | 86 | Lifetime Helpers 87 | ^^^^^^^^^^^^^^^^ 88 | 89 | .. class:: CallbackLifetimeHandle 90 | 91 | Some callbacks, notably those in :class:`AstBuild`, need to outlive the 92 | function call to which they're passed. These callback return a callback 93 | handle that must be kept alive until the callback is no longer needed. 94 | 95 | Global Data 96 | ^^^^^^^^^^^ 97 | 98 | .. data:: DEFAULT_CONTEXT 99 | 100 | ISL objects being unpickled or initialized from strings will be instantiated 101 | within this :class:`Context`. 102 | 103 | .. versionadded:: 2015.2 104 | 105 | Symbolic Constants 106 | ^^^^^^^^^^^^^^^^^^ 107 | 108 | .. autoclass:: error 109 | :members: 110 | :undoc-members: 111 | :exclude-members: names, values, @entries 112 | 113 | .. autoclass:: dim_type 114 | :members: 115 | :undoc-members: 116 | :exclude-members: names, values, @entries 117 | 118 | .. autoclass:: fold 119 | :members: 120 | :undoc-members: 121 | :exclude-members: names, values, @entries 122 | 123 | .. autoclass:: format 124 | :members: 125 | :undoc-members: @entries 126 | 127 | .. autoclass:: yaml_style 128 | :members: 129 | :undoc-members: @entries 130 | 131 | Output 132 | ^^^^^^ 133 | 134 | .. autoclass:: Printer 135 | :members: 136 | 137 | Helper functions 138 | ^^^^^^^^^^^^^^^^ 139 | .. autoclass:: AlignableT 140 | .. autoclass:: AlignableT2 141 | 142 | .. autofunction:: align_spaces 143 | .. autofunction:: align_two 144 | 145 | Canonical Names for Internal Module 146 | ----------------------------------- 147 | 148 | .. :: 149 | 150 | This should switch to using ``:canonical:`` once Sphinx 4.0 is released. 151 | 152 | .. currentmodule:: islpy._isl 153 | 154 | .. class:: stat 155 | 156 | A status result. 157 | 158 | .. class:: error 159 | 160 | See :class:`islpy.error`. 161 | 162 | .. class:: dim_type 163 | 164 | See :class:`islpy.dim_type`. 165 | 166 | .. class:: fold 167 | 168 | See :class:`islpy.fold`. 169 | 170 | .. class:: format 171 | 172 | See :class:`islpy.format`. 173 | 174 | .. class:: yaml_style 175 | 176 | See :class:`islpy.yaml_style`. 177 | 178 | .. class:: Printer 179 | 180 | See :class:`islpy.Printer`. 181 | 182 | .. currentmodule:: islpy._monkeypatch 183 | 184 | .. class:: SetLikeT 185 | 186 | A type variable with an upper bound of :class:`islpy.BasicSet` | :class:`islpy.Set`. 187 | 188 | .. class:: AffOrConstraintT 189 | 190 | A type variable with an upper bound of :class:`islpy.Aff` | :class:`islpy.Constraint`. 191 | 192 | .. class:: BasicT 193 | 194 | A type variable with an upper bound of :class:`islpy.BasicSet` | :class:`islpy.BasicMap`. 195 | 196 | .. class:: SetOrMapT 197 | 198 | A type variable with comprising :class:`islpy.BasicSet` | :class:`islpy.Set` | 199 | :class:`islpy.BasicMap` | :class:`islpy.Map`. 200 | 201 | .. vim: sw=4 202 | -------------------------------------------------------------------------------- /src/wrapper/wrap_isl_part1.cpp: -------------------------------------------------------------------------------- 1 | #include "wrap_isl.hpp" 2 | 3 | namespace isl 4 | { 5 | #include "gen-wrap-part1.inc" 6 | 7 | class constants { }; 8 | } 9 | 10 | namespace islpy 11 | { 12 | bool id_eq(isl::id const *self, isl::id const *other) 13 | { 14 | return self == other; 15 | } 16 | 17 | bool id_ne(isl::id const *self, isl::id const *other) 18 | { 19 | return self != other; 20 | } 21 | } 22 | 23 | void islpy_expose_part1(py::module_ &m) 24 | { 25 | py::class_ 26 | wrap_ctx(m, "Context"); 27 | wrap_ctx.def("__init__", 28 | [](isl::ctx *self) 29 | { 30 | isl_ctx *result = isl_ctx_alloc(); 31 | 32 | // Sounds scary, but just means "don't print a message". 33 | // We implement our own error handling. 34 | isl_options_set_on_error(result, ISL_ON_ERROR_CONTINUE); 35 | 36 | if (result) 37 | { 38 | try 39 | { 40 | new(self) isl::ctx(result); 41 | } 42 | catch (...) 43 | { 44 | isl_ctx_free(result); 45 | throw; 46 | } 47 | } 48 | else 49 | PYTHON_ERROR(RuntimeError, "failed to create context"); 50 | }); 51 | wrap_ctx.attr("_base_name") = "ctx"; 52 | wrap_ctx.attr("_isl_name") = "isl_ctx"; 53 | wrap_ctx.def("_is_valid", &isl::ctx::is_valid); 54 | wrap_ctx.def("_reset_instance", &isl::ctx::reset_instance); 55 | wrap_ctx.def("_wraps_same_instance_as", &isl::ctx::wraps_same_instance_as); 56 | 57 | // {{{ lists 58 | 59 | MAKE_WRAP(id_list, IdList); 60 | MAKE_WRAP(val_list, ValList); 61 | MAKE_WRAP(basic_set_list, BasicSetList); 62 | MAKE_WRAP(basic_map_list, BasicMapList); 63 | MAKE_WRAP(set_list, SetList); 64 | MAKE_WRAP(map_list, MapList); 65 | MAKE_WRAP(union_set_list, UnionSetList); 66 | MAKE_WRAP(constraint_list, ConstraintList); 67 | MAKE_WRAP(aff_list, AffList); 68 | MAKE_WRAP(pw_aff_list, PwAffList); 69 | MAKE_WRAP(pw_multi_aff_list, PwMultiAffList); 70 | MAKE_WRAP(ast_expr_list, AstExprList); 71 | MAKE_WRAP(ast_node_list, AstNodeList); 72 | MAKE_WRAP(qpolynomial_list, QPolynomialList); 73 | MAKE_WRAP(pw_qpolynomial_list, PwQPolynomialList); 74 | MAKE_WRAP(pw_qpolynomial_fold_list, PwQPolynomialFoldList); 75 | MAKE_WRAP(union_pw_aff_list, UnionPwAffList); 76 | MAKE_WRAP(union_pw_multi_aff_list, UnionPwMultiAffList); 77 | MAKE_WRAP(union_map_list, UnionMapList); 78 | 79 | // }}} 80 | 81 | // {{{ maps 82 | 83 | MAKE_WRAP(id_to_ast_expr, IdToAstExpr); 84 | 85 | // }}} 86 | 87 | MAKE_WRAP(printer, Printer); 88 | MAKE_WRAP(val, Val); 89 | wrap_val.def("__init__", 90 | [](isl::val *t, long i, isl::ctx *ctx_wrapper) 91 | { 92 | isl_ctx *ctx = nullptr; 93 | if (ctx_wrapper && ctx_wrapper->is_valid()) 94 | ctx = ctx_wrapper->m_data; 95 | if (!ctx) 96 | ctx = isl::get_default_context(); 97 | if (!ctx) 98 | throw isl::error("Val constructor: no context available"); 99 | isl_val *result = isl_val_int_from_si(ctx, i); 100 | if (result) 101 | new (t) isl::val(result); 102 | else 103 | isl::handle_isl_error(ctx, "isl_val_from_si"); 104 | }, py::arg("i"), py::arg("context").none(true)=py::none() 105 | ); 106 | 107 | MAKE_WRAP(multi_val, MultiVal); 108 | MAKE_WRAP(vec, Vec); 109 | MAKE_WRAP(mat, Mat); 110 | MAKE_WRAP(fixed_box, FixedBox); 111 | 112 | MAKE_WRAP(aff, Aff); 113 | 114 | MAKE_WRAP(pw_aff, PwAff); 115 | MAKE_INIT_CONVERTIBLE(aff, pw_aff); 116 | MAKE_TO_METHOD(aff, pw_aff); 117 | 118 | MAKE_WRAP(union_pw_aff, UnionPwAff); 119 | MAKE_INIT_CONVERTIBLE(pw_aff, union_pw_aff); 120 | 121 | MAKE_WRAP(multi_id, MultiId); 122 | 123 | MAKE_WRAP(multi_aff, MultiAff); 124 | 125 | MAKE_WRAP(pw_multi_aff, PwMultiAff); 126 | MAKE_INIT_CONVERTIBLE(multi_aff, pw_multi_aff) 127 | 128 | MAKE_WRAP(union_pw_multi_aff, UnionPwMultiAff); 129 | MAKE_INIT_CONVERTIBLE(pw_multi_aff, union_pw_multi_aff) 130 | 131 | MAKE_WRAP(multi_pw_aff, MultiPwAff); 132 | 133 | MAKE_WRAP(multi_union_pw_aff, MultiUnionPwAff); 134 | 135 | MAKE_WRAP(id, Id); 136 | wrap_id.def("__init__", 137 | [](isl::id *t, const char *name, py::object user, isl::ctx *ctx_wrapper) 138 | { 139 | isl_ctx *ctx = nullptr; 140 | if (ctx_wrapper && ctx_wrapper->is_valid()) 141 | ctx = ctx_wrapper->m_data; 142 | if (!ctx) 143 | ctx = isl::get_default_context(); 144 | if (!ctx) 145 | throw isl::error("Id constructor: no context available"); 146 | Py_INCREF(user.ptr()); 147 | isl_id *result = isl_id_alloc(ctx, name, user.ptr()); 148 | isl_id_set_free_user(result, isl::my_decref); 149 | if (result) 150 | new (t) isl::id(result); 151 | else 152 | isl::handle_isl_error(ctx, "isl_id_alloc"); 153 | }, py::arg("name"), 154 | py::arg("user").none(true)=py::none(), 155 | py::arg("context").none(true)=py::none() 156 | ); 157 | wrap_id.def("__eq__", islpy::id_eq, py::arg("other"), 158 | "__eq__(self, other)\n\n" 159 | ":param self: :class:`Id`\n" 160 | ":param other: :class:`Id`\n" 161 | ":return: bool "); 162 | wrap_id.def("__ne__", islpy::id_ne, py::arg("other"), 163 | "__ne__(self, other)\n\n" 164 | ":param self: :class:`Id`\n" 165 | ":param other: :class:`Id`\n" 166 | ":return: bool "); 167 | 168 | MAKE_WRAP(constraint, Constraint); 169 | 170 | MAKE_WRAP(space, Space); 171 | MAKE_WRAP(local_space, LocalSpace); 172 | MAKE_INIT_CONVERTIBLE(space, local_space); 173 | MAKE_TO_METHOD(space, local_space); 174 | 175 | #include "gen-expose-part1.inc" 176 | } 177 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | schedule: 8 | - cron: '17 3 * * 0' 9 | 10 | concurrency: 11 | group: ${{ github.head_ref || github.ref_name }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | ruff: 16 | name: Ruff 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v6 20 | - uses: actions/setup-python@v6 21 | - name: "Main Script" 22 | run: | 23 | pipx install ruff 24 | ruff check 25 | 26 | pytest: 27 | name: Pytest Linux on Py${{ matrix.python-version }} 28 | runs-on: ubuntu-latest 29 | strategy: 30 | matrix: 31 | python-version: ['3.10', '3.12', '3.x'] 32 | steps: 33 | - uses: actions/checkout@v6 34 | - 35 | uses: actions/setup-python@v6 36 | with: 37 | python-version: ${{ matrix.python-version }} 38 | - name: "Main Script" 39 | run: | 40 | EXTRA_INSTALL="numpy" 41 | curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh 42 | . ./build-and-test-py-project.sh 43 | 44 | basdedpyright: 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v6 48 | - uses: actions/setup-python@v6 49 | with: 50 | python-version: '3.x' 51 | - name: "Main Script" 52 | run: | 53 | curl -L -O https://tiker.net/ci-support-v0 54 | . ./ci-support-v0 55 | build_py_project_in_venv 56 | pip install nanobind typing-extensions basedpyright 57 | (cd stubgen; python stubgen.py) 58 | basedpyright islpy/_isl.pyi islpy/_monkeypatch.py islpy/__init__.py 59 | 60 | examples: 61 | name: Examples Linux on Py${{ matrix.python-version }} 62 | runs-on: ubuntu-latest 63 | strategy: 64 | matrix: 65 | python-version: ['3.10', '3.12', '3.x'] 66 | steps: 67 | - uses: actions/checkout@v6 68 | - 69 | uses: actions/setup-python@v6 70 | with: 71 | python-version: ${{ matrix.python-version }} 72 | - name: "Main Script" 73 | run: | 74 | EXTRA_INSTALL="matplotlib numpy" 75 | curl -L -O https://tiker.net/ci-support-v0 76 | . ./ci-support-v0 77 | build_py_project_in_venv 78 | run_examples 79 | 80 | pytest_mac: 81 | name: Pytest macOS 82 | runs-on: macos-latest 83 | steps: 84 | - uses: actions/checkout@v6 85 | - 86 | uses: actions/setup-python@v6 87 | with: 88 | python-version: '3.x' 89 | - name: "Main Script" 90 | run: | 91 | export MACOS_DEPLOYMENT_TARGET=10.14 92 | curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh 93 | . ./build-and-test-py-project.sh 94 | 95 | docs: 96 | name: Documentation 97 | runs-on: ubuntu-latest 98 | steps: 99 | - uses: actions/checkout@v6 100 | - 101 | uses: actions/setup-python@v6 102 | with: 103 | python-version: '3.x' 104 | - name: "Main Script" 105 | run: | 106 | curl -L -O https://tiker.net/ci-support-v0 107 | . ./ci-support-v0 108 | build_py_project_in_venv 109 | # https://github.com/sphinx-doc/sphinx/issues/9200 110 | CI_SUPPORT_SPHINX_VERSION_SPECIFIER="!=4.0.0" 111 | build_docs 112 | 113 | downstream_tests: 114 | strategy: 115 | matrix: 116 | downstream_project: [loopy] 117 | name: Tests for downstream project ${{ matrix.downstream_project }} 118 | runs-on: ubuntu-latest 119 | steps: 120 | - uses: actions/checkout@v6 121 | - name: "Main Script" 122 | env: 123 | DOWNSTREAM_PROJECT: ${{ matrix.downstream_project }} 124 | run: | 125 | curl -L -O https://tiker.net/ci-support-v0 126 | . ./ci-support-v0 127 | 128 | git clone "https://github.com/inducer/$DOWNSTREAM_PROJECT.git" 129 | cd "$DOWNSTREAM_PROJECT" 130 | echo "*** $DOWNSTREAM_PROJECT version: $(git rev-parse --short HEAD)" 131 | 132 | edit_requirements_txt_for_downstream_in_subdir 133 | sed -i '/islpy/ d' .test-conda-env-py3.yml 134 | 135 | export CONDA_ENVIRONMENT=.test-conda-env-py3.yml 136 | 137 | # Avoid slow or complicated tests in downstream projects 138 | export PYTEST_ADDOPTS="-k 'not (slowtest or octave or mpi)'" 139 | 140 | build_py_project_in_conda_env 141 | test_py_project 142 | 143 | barvinok: 144 | name: "Test barvinok build script" 145 | runs-on: ubuntu-latest 146 | env: 147 | GITHUB_HEAD_REPOSITORY: ${{github.event.pull_request.head.repo.full_name}} 148 | steps: 149 | - uses: actions/checkout@v6 150 | - 151 | uses: actions/setup-python@v6 152 | with: 153 | python-version: "3.x" 154 | - name: "Main Script" 155 | run: | 156 | python3 -m venv .env 157 | source .env/bin/activate 158 | python -m ensurepip 159 | pip install pcpp pytest numpy 160 | ./build-with-barvinok.sh "$HOME/barvinok-build" 161 | (cd test; LD_LIBRARY_PATH="$HOME/barvinok-build/lib" python -m pytest --tb=native -rxsw) 162 | 163 | # vim: sw=4 164 | -------------------------------------------------------------------------------- /doc/misc.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | This command should install :mod:`islpy`:: 5 | 6 | pip install islpy 7 | 8 | For a more manual installation from source, `download the source 9 | `__, unpack it, and say:: 10 | 11 | pip install -v . 12 | 13 | You may also clone its git repository:: 14 | 15 | git clone --recursive https://github.com/inducer/islpy.git 16 | 17 | The following attempts an editable installation, however note 18 | that this may run into various issues and is not well-supported 19 | by the build tools. In particular, build isolation must be turned off:: 20 | 21 | pip install cmake scikit_build_core nanobind pcpp # prerequisites 22 | pip install --no-build-isolation --config-settings=build-dir=build -ve . 23 | 24 | Support 25 | ======= 26 | 27 | You can try posting questions or comments at the 28 | `Github Discussions site `__ 29 | for islpy. 30 | 31 | For a mailing list, please consider using the `isl list 32 | `_ until they tell us to get 33 | lost. 34 | 35 | License 36 | ======= 37 | 38 | islpy is licensed to you under the MIT/X Consortium license: 39 | 40 | Copyright (c) 2011 Andreas Klöckner and Contributors. 41 | 42 | Permission is hereby granted, free of charge, to any person 43 | obtaining a copy of this software and associated documentation 44 | files (the "Software"), to deal in the Software without 45 | restriction, including without limitation the rights to use, 46 | copy, modify, merge, publish, distribute, sublicense, and/or sell 47 | copies of the Software, and to permit persons to whom the 48 | Software is furnished to do so, subject to the following 49 | conditions: 50 | 51 | The above copyright notice and this permission notice shall be 52 | included in all copies or substantial portions of the Software. 53 | 54 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 55 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 56 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 57 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 58 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 59 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 60 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 61 | OTHER DEALINGS IN THE SOFTWARE. 62 | 63 | .. note:: 64 | 65 | * isl and imath, which islpy depends on, are also licensed under the `MIT 66 | license `_. 67 | 68 | * GMP, which used to be a dependency of isl and thus islpy, is no longer 69 | required. (but building against it can optionally be requested) 70 | 71 | Relation with isl's C interface 72 | =============================== 73 | 74 | Nearly all of the bindings to isl are auto-generated, using the following 75 | rules: 76 | 77 | * Follow :pep:`8`. 78 | * Expose the underlying object-oriented structure. 79 | * Remove the `isl_` and `ISL_` prefixes from data types, macros and 80 | function names, replace them with Python namespaces. 81 | * A method `isl_printer_print_set` would thus become 82 | :meth:`islpy.Printer.print_set`. 83 | 84 | See also :ref:`gen-remarks`. 85 | 86 | User-visible Changes 87 | ==================== 88 | 89 | Version 2016.2 90 | -------------- 91 | .. note:: 92 | 93 | This version is currently in development and can be obtained from 94 | islpy's version control. 95 | 96 | * Update for isl 0.17 97 | * Add :func:`islpy.make_zero_and_vars` 98 | 99 | Version 2016.1.1 100 | ---------------- 101 | 102 | * Add :func:`islpy.make_zero_and_vars` 103 | * Do not turn on small-integer optimization by default 104 | (to avoid build trouble on old compilers) 105 | 106 | Version 2016.1 107 | -------------- 108 | 109 | * Update for isl 0.16 110 | 111 | Version 2014.2.1 112 | ---------------- 113 | 114 | * :mod:`islpy` now avoids using 2to3 for Python 3 compatibility. 115 | 116 | Version 2014.2 117 | -------------- 118 | 119 | * A large number of previously unavailable functions are now exposed. 120 | 121 | * Sebastian Pop's `imath `__ support has 122 | been merged into the version of isl that ships with :mod:`islpy`. This means 123 | that unless a user specifically requests a build against GMP, :mod:`islpy` 124 | is (a) entirely self-contained and depends only on a C++ compiler and 125 | (b) is entirely MIT-licensed by default. 126 | 127 | Version 2014.1 128 | -------------- 129 | 130 | * Many classes are now picklable. 131 | 132 | * isl's handling of integer's has changed, forcing islpy to make 133 | incompatible changes as well. 134 | 135 | Now :class:`islpy.Val` is used to represent all numbers going 136 | into and out of :mod:`islpy`. ``gmpy`` is no longer a dependency 137 | of :mod:`islpy`. The following rules apply for this interface change: 138 | 139 | * You can pass (up to ``long int``-sized) integers to methods of 140 | isl objects without manual conversion to :class:`islpy.Val`. 141 | For larger numbers, you need to convert manually for now. 142 | 143 | * All numbers returned from :mod:`islpy` will be of type :class:`islpy.Val`. 144 | If they are integers, they can be converted 145 | 146 | * Since upstream made the decision to make ``isl_XXX_do_something_val`` 147 | not always semantically equivalent to ``isl_XXX_do_something``, the 148 | old functions were removed. 149 | 150 | One example of this is ``isl_aff_get_constant``, which returned just 151 | the constant, and ``isl_aff_get_constant_val``, which returns the 152 | constant divided by the :class:`islpy.Aff`'s denominator as a rational 153 | value. 154 | 155 | Version 2011.3 156 | -------------- 157 | 158 | * Add :meth:`islpy.Set.project_out_except` and friends. 159 | * Add ``islpy.Set.remove_divs_of_dim_type`` and friends. 160 | * ``islpy.Dim`` was renamed to :class:`islpy.Space` in isl. 161 | * ``islpy.Div`` was removed and replaced by :class:`islpy.Aff` 162 | wherever it was used previously. 163 | * ``islpy.BasicSet.as_set` 164 | and 165 | ``islpy.BasicMap.as_map`` 166 | were removed. 167 | * :ref:`automatic-casts` were added. 168 | * Support for more Python :class:`set`-like behavior was added. In particular, 169 | the operators `|`, `&', '-', `<`, `<=`, `>`, `>=`, `==`, `!=` work as expected. 170 | * Support direct construction from string for objects that have a `read_from_str` 171 | method. 172 | * The constant in a :class:`islpy.Constraint` is now set as the '1' 173 | key in a coefficient dictionary in 174 | :meth:`islpy.Constraint.eq_from_names`, 175 | :meth:`islpy.Constraint.ineq_from_names`, and 176 | :meth:`islpy.Constraint.set_coefficients_by_name`. 177 | 178 | Version 2011.2 179 | -------------- 180 | 181 | * Switch to copy-by-default semantics. 182 | * A few changes in Python-side functionality. 183 | * Automatic type promotion in 'self' argument. 184 | 185 | Version 2011.1 186 | -------------- 187 | 188 | * Initial release. 189 | 190 | Documentation Cross-References 191 | ------------------------------ 192 | 193 | .. class:: unsigned 194 | 195 | See :class:`int`. 196 | 197 | .. class:: long 198 | 199 | See :class:`int`. 200 | 201 | .. class:: size_t 202 | 203 | See :class:`int`. 204 | 205 | .. class:: double 206 | 207 | See :class:`float`. 208 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Useful setting for looking at build commands (passed to pip install): 2 | # --config-settings=cmake.define.CMAKE_VERBOSE_MAKEFILE=ON 3 | # 4 | # To build with debug info: Run pip install with 5 | # --config-settings=cmake.build-type=Debug 6 | # Note that setting CMAKE_BUILD_TYPE to Debug here does not suffice: 7 | # scikit build core will still silently strip the debug symbols: 8 | # https://github.com/scikit-build/scikit-build-core/issues/875 9 | 10 | cmake_minimum_required(VERSION 3.15...3.27) 11 | project(islpy) 12 | find_package(Python 3.10 COMPONENTS Interpreter Development.Module REQUIRED) 13 | 14 | # Detect the installed nanobind package and import it into CMake 15 | execute_process( 16 | COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir 17 | OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE NB_DIR) 18 | list(APPEND CMAKE_PREFIX_PATH "${NB_DIR}") 19 | find_package(nanobind CONFIG REQUIRED) 20 | 21 | option(USE_SHIPPED_ISL "Use shipped ISL" ON) 22 | option(USE_SHIPPED_IMATH "Use shipped IMATH" ON) 23 | option(USE_IMATH_FOR_MP "Use IMATH for multiprecision arithmetic" ON) 24 | option(USE_IMATH_SIO "Use IMATH small-integer optimization" ON) 25 | option(USE_GMP_FOR_MP "Use GMP" OFF) 26 | option(USE_BARVINOK "Use Barvinok (beware of GPL license)" OFF) 27 | option(GENERATE_STUBS "Generate stubs as part of build" ON) 28 | 29 | if(USE_SHIPPED_ISL) 30 | if(USE_BARVINOK) 31 | message(FATAL_ERROR "Using barvinok is not compatible with shipped ISL") 32 | endif() 33 | set(ISL_SOURCES 34 | isl/isl_schedule.c 35 | isl/isl_ast_build_expr.c 36 | isl/isl_sample.c 37 | isl/isl_coalesce.c 38 | isl/isl_fold.c 39 | isl/isl_schedule_read.c 40 | isl/isl_aff_map.c 41 | isl/isl_scheduler_clustering.c 42 | isl/isl_flow.c 43 | isl/isl_map_subtract.c 44 | isl/uset_to_umap.c 45 | isl/isl_hash.c 46 | isl/isl_aff.c 47 | isl/isl_transitive_closure.c 48 | isl/isl_map_simplify.c 49 | isl/print.c 50 | isl/basis_reduction_tab.c 51 | isl/isl_schedule_constraints.c 52 | isl/isl_sort.c 53 | isl/isl_ast.c 54 | isl/bset_to_bmap.c 55 | isl/bset_from_bmap.c 56 | isl/isl_schedule_band.c 57 | isl/isl_bernstein.c 58 | isl/uset_from_umap.c 59 | isl/isl_scheduler.c 60 | isl/isl_set_to_ast_graft_list.c 61 | isl/isl_convex_hull.c 62 | isl/isl_schedule_tree.c 63 | isl/isl_tarjan.c 64 | isl/isl_equalities.c 65 | isl/isl_constraint.c 66 | isl/isl_union_map.c 67 | isl/isl_bound.c 68 | isl/isl_stride.c 69 | isl/set_list_from_map_list_inl.c 70 | isl/isl_farkas.c 71 | isl/isl_tab_pip.c 72 | isl/set_to_map.c 73 | isl/set_from_map.c 74 | isl/isl_lp.c 75 | isl/isl_ffs.c 76 | isl/isl_id_to_ast_expr.c 77 | isl/isl_val.c 78 | isl/isl_set_list.c 79 | isl/isl_space.c 80 | isl/isl_tab.c 81 | isl/isl_map.c 82 | isl/isl_version.c 83 | isl/isl_stream.c 84 | isl/isl_local_space.c 85 | isl/isl_id_to_pw_aff.c 86 | isl/isl_ilp.c 87 | isl/isl_range.c 88 | isl/isl_point.c 89 | isl/isl_schedule_node.c 90 | isl/isl_polynomial.c 91 | isl/isl_options.c 92 | isl/isl_morph.c 93 | isl/isl_deprecated.c 94 | isl/isl_ctx.c 95 | isl/isl_seq.c 96 | isl/isl_box.c 97 | isl/isl_output.c 98 | isl/isl_factorization.c 99 | isl/isl_printer.c 100 | isl/dep.c 101 | isl/isl_id_to_id.c 102 | isl/isl_ast_build.c 103 | isl/isl_ast_codegen.c 104 | isl/isl_obj.c 105 | isl/isl_scheduler_scc.c 106 | isl/isl_vec.c 107 | isl/isl_map_list.c 108 | isl/isl_vertices.c 109 | isl/isl_arg.c 110 | isl/isl_mat.c 111 | isl/isl_id.c 112 | isl/isl_affine_hull.c 113 | isl/isl_scan.c 114 | isl/isl_map_to_basic_set.c 115 | isl/isl_blk.c 116 | isl/isl_dim_map.c 117 | isl/isl_local.c 118 | isl/isl_reordering.c 119 | isl/isl_ast_graft.c 120 | isl/isl_input.c 121 | ) 122 | set(ISL_INC_DIRS 123 | ${CMAKE_SOURCE_DIR}/isl-supplementary 124 | ${CMAKE_SOURCE_DIR}/isl/include 125 | ${CMAKE_SOURCE_DIR}/isl 126 | ) 127 | if(USE_GMP_FOR_MP) 128 | list(APPEND ISL_SOURCES 129 | isl/isl_val_gmp.c 130 | ) 131 | elseif(USE_IMATH_FOR_MP) 132 | if(USE_SHIPPED_IMATH) 133 | list(APPEND ISL_SOURCES 134 | isl/isl_imath.c 135 | isl/imath/imath.c 136 | isl/imath/imrat.c 137 | isl/imath/gmp_compat.c 138 | ) 139 | list(APPEND ISL_INC_DIRS ${CMAKE_SOURCE_DIR}/isl/imath) 140 | endif() 141 | if(USE_IMATH_SIO) 142 | list(APPEND ISL_SOURCES 143 | isl/isl_int_sioimath.c 144 | isl/isl_val_sioimath.c 145 | ) 146 | else() 147 | list(APPEND ISL_SOURCES 148 | isl/isl_val_imath.c 149 | ) 150 | endif() 151 | endif() 152 | else() 153 | set(ISL_SOURCES) 154 | if(NOT ISL_LIB_NAMES) 155 | set(ISL_LIB_NAMES isl) 156 | if(USE_BARVINOK) 157 | list(PREPEND ISL_LIB_NAMES barvinok) 158 | endif() 159 | endif() 160 | endif() 161 | 162 | set(ISLPY_GENERATED_SOURCE 163 | ${CMAKE_BINARY_DIR}/generated/gen-expose-part1.inc 164 | ${CMAKE_BINARY_DIR}/generated/gen-expose-part2.inc 165 | ${CMAKE_BINARY_DIR}/generated/gen-expose-part3.inc 166 | ${CMAKE_BINARY_DIR}/generated/gen-wrap-part1.inc 167 | ${CMAKE_BINARY_DIR}/generated/gen-wrap-part2.inc 168 | ${CMAKE_BINARY_DIR}/generated/gen-wrap-part3.inc 169 | ) 170 | 171 | if(USE_BARVINOK) 172 | set(ISLPY_GENERATION_FLAGS --barvinok) 173 | else() 174 | set(ISLPY_GENERATION_FLAGS) 175 | endif() 176 | 177 | add_custom_command( 178 | OUTPUT ${ISLPY_GENERATED_SOURCE} 179 | COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/gen_wrap.py 180 | -o ${CMAKE_BINARY_DIR}/generated 181 | -I ${ISL_INC_DIRS} 182 | ${ISLPY_GENERATION_FLAGS} 183 | ) 184 | 185 | nanobind_add_module( 186 | _isl 187 | NB_STATIC # Build static libnanobind (the extension module itself remains a shared library) 188 | NOMINSIZE # Optimize for speed, not for size 189 | LTO # Enable LTO 190 | src/wrapper/wrap_isl.cpp 191 | src/wrapper/wrap_isl_part1.cpp 192 | src/wrapper/wrap_isl_part2.cpp 193 | src/wrapper/wrap_isl_part3.cpp 194 | ${ISL_SOURCES} 195 | ${ISLPY_GENERATED_SOURCE} 196 | ) 197 | target_include_directories(_isl PRIVATE ${CMAKE_BINARY_DIR}/generated) 198 | 199 | # Work around https://github.com/inducer/islpy/issues/120. 200 | # See https://stackoverflow.com/questions/43554227/extern-inline-func-results-in-undefined-reference-error 201 | # for some context. 202 | set_source_files_properties(${ISL_SOURCES} PROPERTIES COMPILE_DEFINITIONS __OPTIMIZE_SIZE__) 203 | 204 | if(USE_IMATH_FOR_MP) 205 | target_compile_definitions(_isl PRIVATE USE_IMATH_FOR_MP=1) 206 | endif() 207 | 208 | if(USE_IMATH_SIO) 209 | target_compile_definitions(_isl PRIVATE USE_SMALL_INT_OPT=1) 210 | endif() 211 | 212 | if(USE_GMP_FOR_MP) 213 | target_compile_definitions(_isl PRIVATE USE_GMP_FOR_MP=1) 214 | endif() 215 | 216 | if(USE_BARVINOK) 217 | target_compile_definitions(_isl PRIVATE ISLPY_INCLUDE_BARVINOK=1) 218 | target_include_directories(_isl PRIVATE ${BARVINOK_INC_DIRS}) 219 | target_link_directories(_isl PRIVATE ${BARVINOK_LIB_DIRS}) 220 | target_link_libraries(_isl PRIVATE ${BARVINOK_LIB_NAMES}) 221 | endif() 222 | 223 | target_include_directories(_isl PRIVATE ${ISL_INC_DIRS}) 224 | 225 | if(USE_SHIPPED_ISL) 226 | target_compile_definitions(_isl PRIVATE GIT_HEAD_ID="included-with-islpy") 227 | else() 228 | target_link_directories(_isl PRIVATE ${ISL_LIB_DIRS}) 229 | target_link_libraries(_isl PRIVATE ${ISL_LIB_NAMES}) 230 | endif() 231 | 232 | install(TARGETS _isl LIBRARY DESTINATION islpy) 233 | 234 | if(GENERATE_STUBS) 235 | set(ISLPY_STUB_FILE ${CMAKE_BINARY_DIR}/_isl.pyi) 236 | add_custom_command( 237 | OUTPUT ${ISLPY_STUB_FILE} 238 | COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/stubgen/stubgen.py 239 | -o ${CMAKE_BINARY_DIR} 240 | --exec ${CMAKE_SOURCE_DIR}/islpy/_monkeypatch.py 241 | --python-path ${CMAKE_BINARY_DIR} 242 | -m _isl 243 | DEPENDS _isl 244 | ) 245 | add_custom_target( 246 | _isl_stub 247 | ALL DEPENDS ${CMAKE_BINARY_DIR}/_isl.pyi 248 | ) 249 | install(FILES ${ISLPY_STUB_FILE} DESTINATION islpy) 250 | else() 251 | if(NOT EXISTS "${CMAKE_SOURCE_DIR}/islpy/_isl.pyi") 252 | message(FATAL_ERROR "Disabled stub generation requires pregenerated stubs") 253 | endif() 254 | endif() 255 | 256 | # vim: sw=2 257 | -------------------------------------------------------------------------------- /src/wrapper/wrap_isl.cpp: -------------------------------------------------------------------------------- 1 | #include "wrap_isl.hpp" 2 | 3 | void islpy_expose_part1(py::module_ &m); 4 | void islpy_expose_part2(py::module_ &m); 5 | void islpy_expose_part3(py::module_ &m); 6 | 7 | namespace isl 8 | { 9 | ctx_use_map_t ctx_use_map; 10 | 11 | [[noreturn]] void handle_isl_error(isl_ctx *ctx, std::string const &func_name) 12 | { 13 | std::string errmsg = "call to " + func_name + " failed: "; 14 | if (ctx) 15 | { 16 | const char *isl_msg = isl_ctx_last_error_msg(ctx); 17 | if (isl_msg) 18 | errmsg += isl_msg; 19 | else 20 | errmsg += ""; 21 | 22 | const char *err_file = isl_ctx_last_error_file(ctx); 23 | if (err_file) 24 | { 25 | errmsg += " in "; 26 | errmsg += err_file; 27 | errmsg += ":"; 28 | errmsg += std::to_string(isl_ctx_last_error_line(ctx)); 29 | } 30 | } 31 | throw isl::error(errmsg); 32 | } 33 | 34 | isl_ctx *get_default_context() 35 | { 36 | py::module_ mod = py::module_::import_("islpy"); 37 | py::object ctx_py = mod.attr("DEFAULT_CONTEXT"); 38 | if (!ctx_py.is_none()) 39 | { 40 | isl::ctx *ctx_wrapper = py::cast(ctx_py); 41 | if (ctx_wrapper->is_valid()) 42 | return ctx_wrapper->m_data; 43 | } 44 | return nullptr; 45 | } 46 | 47 | // bogus, unused, just in service of type annotation 48 | struct callback_lifetime_handle { }; 49 | } 50 | 51 | 52 | NB_MODULE(_isl, m) 53 | { 54 | // py::options options; 55 | // options.disable_function_signatures(); 56 | 57 | static py::exception ISLError(m, "Error"); 58 | 59 | py::register_exception_translator( 60 | [](const std::exception_ptr &p, void * /* unused */) 61 | { 62 | try 63 | { 64 | std::rethrow_exception(p); 65 | } 66 | catch (const isl::error &e) 67 | { 68 | PyErr_SetString(ISLError.ptr(), e.what()); 69 | } 70 | }); 71 | 72 | // py::docstring_options doc_opt(true, false, false); 73 | 74 | /* 75 | { 76 | typedef isl_options cls; 77 | py::class_(m, "Options") 78 | .DEF_SIMPLE_RW_MEMBER(lp_solver) 79 | .DEF_SIMPLE_RW_MEMBER(ilp_solver) 80 | .DEF_SIMPLE_RW_MEMBER(pip) 81 | .DEF_SIMPLE_RW_MEMBER(context) 82 | .DEF_SIMPLE_RW_MEMBER(gbr) 83 | .DEF_SIMPLE_RW_MEMBER(gbr_only_first) 84 | .DEF_SIMPLE_RW_MEMBER(closure) 85 | .DEF_SIMPLE_RW_MEMBER(bound) 86 | .DEF_SIMPLE_RW_MEMBER(bernstein_recurse) 87 | .DEF_SIMPLE_RW_MEMBER(bernstein_triangulate) 88 | .DEF_SIMPLE_RW_MEMBER(pip_symmetry) 89 | .DEF_SIMPLE_RW_MEMBER(convex) 90 | .DEF_SIMPLE_RW_MEMBER(schedule_parametric) 91 | .DEF_SIMPLE_RW_MEMBER(schedule_outer_zero_distance) 92 | .DEF_SIMPLE_RW_MEMBER(schedule_split_parallel) 93 | ; 94 | } 95 | */ 96 | 97 | py::enum_(m, "error") 98 | .ENUM_VALUE(isl_error_, none) 99 | .ENUM_VALUE(isl_error_, abort) 100 | .ENUM_VALUE(isl_error_, alloc) 101 | .ENUM_VALUE(isl_error_, unknown) 102 | .ENUM_VALUE(isl_error_, internal) 103 | .ENUM_VALUE(isl_error_, invalid) 104 | .ENUM_VALUE(isl_error_, quota) 105 | .ENUM_VALUE(isl_error_, unsupported) 106 | ; 107 | 108 | py::enum_(m, "stat") 109 | .ENUM_VALUE(isl_stat_, error) 110 | .ENUM_VALUE(isl_stat_, ok) 111 | ; 112 | 113 | // Arithmetic (i.e. export numerical values) to ensure that out == set, as on 114 | // the C side. 115 | py::enum_(m, "dim_type", py::is_arithmetic()) 116 | .ENUM_VALUE(isl_dim_, cst) 117 | .ENUM_VALUE(isl_dim_, param) 118 | .value("in_", isl_dim_in) 119 | .ENUM_VALUE(isl_dim_, out) 120 | .ENUM_VALUE(isl_dim_, set) 121 | .ENUM_VALUE(isl_dim_, div) 122 | .ENUM_VALUE(isl_dim_, all) 123 | ; 124 | 125 | py::enum_(m, "schedule_node_type") 126 | .ENUM_VALUE(isl_schedule_node_, error) 127 | .ENUM_VALUE(isl_schedule_node_, band) 128 | .ENUM_VALUE(isl_schedule_node_, context) 129 | .ENUM_VALUE(isl_schedule_node_, domain) 130 | .ENUM_VALUE(isl_schedule_node_, expansion) 131 | .ENUM_VALUE(isl_schedule_node_, extension) 132 | .ENUM_VALUE(isl_schedule_node_, filter) 133 | .ENUM_VALUE(isl_schedule_node_, leaf) 134 | .ENUM_VALUE(isl_schedule_node_, guard) 135 | .ENUM_VALUE(isl_schedule_node_, mark) 136 | .ENUM_VALUE(isl_schedule_node_, sequence) 137 | .ENUM_VALUE(isl_schedule_node_, set) 138 | ; 139 | 140 | py::enum_(m, "ast_expr_op_type") 141 | .ENUM_VALUE(isl_ast_expr_op_, error) 142 | .value("and_", isl_ast_expr_op_and) 143 | .ENUM_VALUE(isl_ast_expr_op_, and_then) 144 | .value("or_", isl_ast_expr_op_or) 145 | .ENUM_VALUE(isl_ast_expr_op_, or_else) 146 | .ENUM_VALUE(isl_ast_expr_op_, max) 147 | .ENUM_VALUE(isl_ast_expr_op_, min) 148 | .ENUM_VALUE(isl_ast_expr_op_, minus) 149 | .ENUM_VALUE(isl_ast_expr_op_, add) 150 | .ENUM_VALUE(isl_ast_expr_op_, sub) 151 | .ENUM_VALUE(isl_ast_expr_op_, mul) 152 | .ENUM_VALUE(isl_ast_expr_op_, div) 153 | .ENUM_VALUE(isl_ast_expr_op_, fdiv_q) 154 | .ENUM_VALUE(isl_ast_expr_op_, pdiv_q) 155 | .ENUM_VALUE(isl_ast_expr_op_, pdiv_r) 156 | .ENUM_VALUE(isl_ast_expr_op_, zdiv_r) 157 | .ENUM_VALUE(isl_ast_expr_op_, cond) 158 | .ENUM_VALUE(isl_ast_expr_op_, select) 159 | .ENUM_VALUE(isl_ast_expr_op_, eq) 160 | .ENUM_VALUE(isl_ast_expr_op_, le) 161 | .ENUM_VALUE(isl_ast_expr_op_, lt) 162 | .ENUM_VALUE(isl_ast_expr_op_, ge) 163 | .ENUM_VALUE(isl_ast_expr_op_, gt) 164 | .ENUM_VALUE(isl_ast_expr_op_, call) 165 | .ENUM_VALUE(isl_ast_expr_op_, access) 166 | .ENUM_VALUE(isl_ast_expr_op_, member) 167 | .ENUM_VALUE(isl_ast_expr_op_, address_of) 168 | ; 169 | 170 | py::enum_(m, "fold") 171 | .ENUM_VALUE(isl_fold_, min) 172 | .ENUM_VALUE(isl_fold_, max) 173 | .ENUM_VALUE(isl_fold_, list) 174 | ; 175 | 176 | py::enum_(m, "ast_expr_type") 177 | .ENUM_VALUE(isl_ast_expr_, error) 178 | .ENUM_VALUE(isl_ast_expr_, op) 179 | .ENUM_VALUE(isl_ast_expr_, id) 180 | .ENUM_VALUE(isl_ast_expr_, int) 181 | ; 182 | 183 | py::enum_(m, "ast_node_type") 184 | .ENUM_VALUE(isl_ast_node_, error) 185 | .value("for_", isl_ast_node_for) 186 | .value("if_", isl_ast_node_if) 187 | .ENUM_VALUE(isl_ast_node_, block) 188 | .ENUM_VALUE(isl_ast_node_, user) 189 | .ENUM_VALUE(isl_ast_node_, mark) 190 | ; 191 | 192 | py::enum_(m, "ast_loop_type") 193 | .ENUM_VALUE(isl_ast_loop_, error) 194 | .ENUM_VALUE(isl_ast_loop_, default) 195 | .ENUM_VALUE(isl_ast_loop_, atomic) 196 | .ENUM_VALUE(isl_ast_loop_, unroll) 197 | .ENUM_VALUE(isl_ast_loop_, separate) 198 | ; 199 | 200 | #define ADD_MACRO_ATTR(cls_name, prefix, name) cls_name.attr(#name) = prefix##name 201 | 202 | py::class_ cls_format(m, "format"); 203 | ADD_MACRO_ATTR(cls_format, ISL_FORMAT_, ISL); 204 | ADD_MACRO_ATTR(cls_format, ISL_FORMAT_, POLYLIB); 205 | ADD_MACRO_ATTR(cls_format, ISL_FORMAT_, POLYLIB_CONSTRAINTS); 206 | ADD_MACRO_ATTR(cls_format, ISL_FORMAT_, OMEGA); 207 | ADD_MACRO_ATTR(cls_format, ISL_FORMAT_, C); 208 | ADD_MACRO_ATTR(cls_format, ISL_FORMAT_, LATEX); 209 | ADD_MACRO_ATTR(cls_format, ISL_FORMAT_, EXT_POLYLIB); 210 | 211 | py::class_ cls_yaml_style(m, "yaml_style"); 212 | ADD_MACRO_ATTR(cls_yaml_style, ISL_YAML_STYLE_, BLOCK); 213 | ADD_MACRO_ATTR(cls_yaml_style, ISL_YAML_STYLE_, FLOW); 214 | 215 | py::class_ cls_bound(m, "bound"); 216 | ADD_MACRO_ATTR(cls_bound, ISL_BOUND_, BERNSTEIN); 217 | ADD_MACRO_ATTR(cls_bound, ISL_BOUND_, RANGE); 218 | 219 | py::class_ cls_on_error(m, "on_error"); 220 | ADD_MACRO_ATTR(cls_on_error, ISL_ON_ERROR_, WARN); 221 | ADD_MACRO_ATTR(cls_on_error, ISL_ON_ERROR_, CONTINUE); 222 | ADD_MACRO_ATTR(cls_on_error, ISL_ON_ERROR_, ABORT); 223 | 224 | py::class_ cls_schedule_algorithm(m, "schedule_algorithm"); 225 | ADD_MACRO_ATTR(cls_schedule_algorithm, ISL_SCHEDULE_ALGORITHM_, ISL); 226 | ADD_MACRO_ATTR(cls_schedule_algorithm, ISL_SCHEDULE_ALGORITHM_, FEAUTRIER); 227 | 228 | m.def("isl_version", [] () { return isl_version(); }); 229 | 230 | py::class_ wrap_cb_lifetime_handle(m, "CallbackLifetimeHandle"); 231 | 232 | islpy_expose_part1(m); 233 | islpy_expose_part2(m); 234 | islpy_expose_part3(m); 235 | 236 | py::implicitly_convertible(); 237 | 238 | py::implicitly_convertible(); 239 | 240 | py::implicitly_convertible(); 241 | 242 | // As far as I can tell, the reported leaks stem from the fact that we copy 243 | // many wrapper-exposed symbols from the wrapper namespace (islpy._isl) to 244 | // islpy, which keeps these alive past shutdown of the wrapper module (though 245 | // they should get cleaned up eventually!). -AK, 2023-09-08 246 | py::set_leak_warnings(false); 247 | } 248 | -------------------------------------------------------------------------------- /src/wrapper/wrap_isl.hpp: -------------------------------------------------------------------------------- 1 | #include "wrap_helpers.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifdef ISLPY_INCLUDE_BARVINOK 29 | #include 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | // TODO: flow.h 40 | // TODO: better error reporting 41 | 42 | namespace py = nanobind; 43 | 44 | namespace isl 45 | { 46 | [[noreturn]] void handle_isl_error(isl_ctx *ctx, std::string const &func_name); 47 | isl_ctx *get_default_context(); 48 | 49 | class error : public std::runtime_error 50 | { 51 | public: 52 | explicit error (const std::string &what) 53 | : std::runtime_error(what) 54 | { } 55 | }; 56 | 57 | struct ctx; 58 | 59 | typedef std::unordered_map ctx_use_map_t; 60 | extern ctx_use_map_t ctx_use_map; 61 | 62 | inline void ref_ctx(isl_ctx *data) 63 | { 64 | ctx_use_map_t::iterator it(ctx_use_map.find(data)); 65 | if (it == ctx_use_map.end()) 66 | ctx_use_map[data] = 1; 67 | else 68 | (it->second)++; 69 | } 70 | 71 | inline void unref_ctx(isl_ctx *ctx) 72 | { 73 | ctx_use_map[ctx] -= 1; 74 | if (ctx_use_map[ctx] == 0) 75 | isl_ctx_free(ctx); 76 | } 77 | 78 | #define WRAP_CLASS(name) \ 79 | struct name { WRAP_CLASS_CONTENT(name) } 80 | 81 | #define MAKE_CAST_CTOR(name, from_type, cast_func) \ 82 | name(from_type const &data) \ 83 | : m_data(nullptr) \ 84 | { \ 85 | isl_##from_type *copy = isl_##from_type##_copy(data.m_data); \ 86 | if (!copy) \ 87 | throw error("isl_" #from_type "_copy failed"); \ 88 | m_data = cast_func(copy); \ 89 | if (!m_data) \ 90 | throw error(#cast_func " failed"); \ 91 | \ 92 | ref_ctx(get_ctx()); \ 93 | } 94 | 95 | #define WRAP_CLASS_CONTENT(name) \ 96 | public: \ 97 | isl_##name *m_data; \ 98 | \ 99 | name(isl_##name *data) \ 100 | : m_data(nullptr) \ 101 | /* passing nullptr is allowed to create a (temporarily invalid) */ \ 102 | /* instance */ \ 103 | { \ 104 | take_possession_of(data); \ 105 | } \ 106 | \ 107 | isl_ctx *get_ctx() \ 108 | { \ 109 | return isl_##name##_get_ctx(m_data); \ 110 | } \ 111 | \ 112 | void invalidate() \ 113 | { \ 114 | if (m_data) \ 115 | { \ 116 | unref_ctx(get_ctx()); \ 117 | m_data = nullptr; \ 118 | } \ 119 | } \ 120 | \ 121 | bool is_valid() const \ 122 | { \ 123 | return (bool) m_data; \ 124 | } \ 125 | \ 126 | ~name() \ 127 | { \ 128 | free_instance(); \ 129 | } \ 130 | \ 131 | void free_instance() \ 132 | { \ 133 | if (m_data) \ 134 | { \ 135 | unref_ctx(get_ctx()); \ 136 | isl_##name##_free(m_data); \ 137 | m_data = nullptr; \ 138 | } \ 139 | } \ 140 | \ 141 | void take_possession_of(isl_##name *data) \ 142 | { \ 143 | free_instance(); \ 144 | if (data) \ 145 | { \ 146 | m_data = data; \ 147 | ref_ctx(get_ctx()); \ 148 | } \ 149 | } \ 150 | 151 | struct ctx \ 152 | { 153 | public: 154 | isl_ctx *m_data; 155 | 156 | ctx(isl_ctx *data) 157 | : m_data(data) 158 | { 159 | ref_ctx(data); 160 | } 161 | 162 | bool is_valid() const 163 | { 164 | return true; 165 | } 166 | 167 | ~ctx() 168 | { 169 | unref_ctx(m_data); 170 | } 171 | 172 | void reset_instance(ctx &other) 173 | { 174 | ref_ctx(other.m_data); 175 | unref_ctx(m_data); 176 | m_data = other.m_data; 177 | } 178 | 179 | bool wraps_same_instance_as(ctx const &other) 180 | { 181 | return m_data == other.m_data; 182 | } 183 | }; 184 | 185 | // matches order in gen_wrap.py 186 | 187 | // {{{ part 1 188 | 189 | WRAP_CLASS(id_list); 190 | WRAP_CLASS(val_list); 191 | WRAP_CLASS(basic_set_list); 192 | WRAP_CLASS(basic_map_list); 193 | WRAP_CLASS(set_list); 194 | WRAP_CLASS(map_list); 195 | WRAP_CLASS(union_set_list); 196 | WRAP_CLASS(constraint_list); 197 | WRAP_CLASS(aff_list); 198 | WRAP_CLASS(pw_aff_list); 199 | WRAP_CLASS(pw_multi_aff_list); 200 | WRAP_CLASS(ast_expr_list); 201 | WRAP_CLASS(ast_node_list); 202 | WRAP_CLASS(qpolynomial_list); 203 | WRAP_CLASS(pw_qpolynomial_list); 204 | WRAP_CLASS(pw_qpolynomial_fold_list); 205 | WRAP_CLASS(union_pw_aff_list); 206 | WRAP_CLASS(union_pw_multi_aff_list); 207 | WRAP_CLASS(union_map_list); 208 | 209 | WRAP_CLASS(id_to_ast_expr); 210 | 211 | WRAP_CLASS(printer); 212 | WRAP_CLASS(val); 213 | WRAP_CLASS(multi_val); 214 | WRAP_CLASS(vec); 215 | WRAP_CLASS(mat); 216 | WRAP_CLASS(fixed_box); 217 | 218 | WRAP_CLASS(aff); 219 | struct pw_aff 220 | { 221 | WRAP_CLASS_CONTENT(pw_aff); 222 | MAKE_CAST_CTOR(pw_aff, aff, isl_pw_aff_from_aff); 223 | }; 224 | struct union_pw_aff 225 | { 226 | WRAP_CLASS_CONTENT(union_pw_aff); 227 | MAKE_CAST_CTOR(union_pw_aff, pw_aff, isl_union_pw_aff_from_pw_aff); 228 | }; 229 | 230 | WRAP_CLASS(multi_aff); 231 | struct pw_multi_aff 232 | { 233 | WRAP_CLASS_CONTENT(pw_multi_aff); 234 | MAKE_CAST_CTOR(pw_multi_aff, multi_aff, isl_pw_multi_aff_from_multi_aff); 235 | }; 236 | struct union_pw_multi_aff 237 | { 238 | WRAP_CLASS_CONTENT(union_pw_multi_aff); 239 | MAKE_CAST_CTOR(union_pw_multi_aff, pw_multi_aff, isl_union_pw_multi_aff_from_pw_multi_aff); 240 | }; 241 | 242 | WRAP_CLASS(multi_pw_aff); 243 | WRAP_CLASS(multi_union_pw_aff); 244 | 245 | WRAP_CLASS(id); 246 | WRAP_CLASS(multi_id); 247 | 248 | WRAP_CLASS(constraint); 249 | WRAP_CLASS(space); 250 | struct local_space 251 | { 252 | WRAP_CLASS_CONTENT(local_space); 253 | MAKE_CAST_CTOR(local_space, space, isl_local_space_from_space); 254 | }; 255 | 256 | // }}} 257 | 258 | // {{{ part 2 259 | 260 | WRAP_CLASS(basic_set); 261 | WRAP_CLASS(basic_map); 262 | 263 | struct set 264 | { 265 | WRAP_CLASS_CONTENT(set); 266 | MAKE_CAST_CTOR(set, basic_set, isl_set_from_basic_set); 267 | }; 268 | 269 | struct map 270 | { 271 | WRAP_CLASS_CONTENT(map); 272 | MAKE_CAST_CTOR(map, basic_map, isl_map_from_basic_map); 273 | }; 274 | 275 | struct union_set 276 | { 277 | WRAP_CLASS_CONTENT(union_set); 278 | MAKE_CAST_CTOR(union_set, set, isl_union_set_from_set); 279 | MAKE_CAST_CTOR(union_set, basic_set, isl_union_set_from_basic_set); 280 | }; 281 | 282 | struct union_map 283 | { 284 | WRAP_CLASS_CONTENT(union_map); 285 | MAKE_CAST_CTOR(union_map, map, isl_union_map_from_map); 286 | MAKE_CAST_CTOR(union_map, basic_map, isl_union_map_from_basic_map); 287 | }; 288 | 289 | WRAP_CLASS(point); 290 | WRAP_CLASS(vertex); 291 | WRAP_CLASS(cell); 292 | WRAP_CLASS(vertices); 293 | WRAP_CLASS(stride_info); 294 | 295 | // }}} 296 | 297 | // {{{ part 3 298 | 299 | WRAP_CLASS(qpolynomial); 300 | WRAP_CLASS(pw_qpolynomial); 301 | WRAP_CLASS(qpolynomial_fold); 302 | WRAP_CLASS(pw_qpolynomial_fold); 303 | WRAP_CLASS(union_pw_qpolynomial); 304 | WRAP_CLASS(union_pw_qpolynomial_fold); 305 | WRAP_CLASS(term); 306 | 307 | WRAP_CLASS(schedule); 308 | WRAP_CLASS(schedule_constraints); 309 | WRAP_CLASS(schedule_node); 310 | 311 | WRAP_CLASS(access_info); 312 | WRAP_CLASS(flow); 313 | WRAP_CLASS(restriction); 314 | WRAP_CLASS(union_access_info); 315 | WRAP_CLASS(union_flow); 316 | 317 | WRAP_CLASS(ast_expr); 318 | WRAP_CLASS(ast_node); 319 | WRAP_CLASS(ast_print_options); 320 | WRAP_CLASS(ast_build); 321 | 322 | // }}} 323 | 324 | class format { }; 325 | class yaml_style { }; 326 | class bound { }; 327 | class on_error { }; 328 | class schedule_algorithm { }; 329 | 330 | inline void my_decref(void *user) 331 | { 332 | Py_DECREF((PyObject *) user); 333 | } 334 | } 335 | 336 | 337 | 338 | 339 | 340 | #define MAKE_WRAP(name, py_name) \ 341 | py::class_ wrap_##name(m, #py_name, py::dynamic_attr()); \ 342 | wrap_##name.def("_is_valid", &isl::name::is_valid); \ 343 | wrap_##name.attr("_base_name") = #name; \ 344 | wrap_##name.attr("_isl_name") = "isl_"#name; \ 345 | 346 | #define MAKE_TO_METHOD(from, to) \ 347 | wrap_##from.def("to_" #to, [](isl::from const &s) { return new isl::to(s); }); 348 | #define MAKE_INIT_CONVERTIBLE(from, to) \ 349 | wrap_##to.def(py::init_implicit()); 350 | 351 | 352 | // vim: foldmethod=marker 353 | -------------------------------------------------------------------------------- /islpy/__init__.py: -------------------------------------------------------------------------------- 1 | __copyright__ = "Copyright (C) 2011-20 Andreas Kloeckner" 2 | 3 | __license__ = """ 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | """ 22 | 23 | from collections.abc import Collection, Sequence 24 | from typing import Literal, TypeAlias, TypeVar, cast 25 | 26 | from islpy.version import VERSION, VERSION_TEXT 27 | 28 | 29 | __version__ = VERSION_TEXT 30 | 31 | # {{{ name imports 32 | 33 | from islpy._isl import ( 34 | AccessInfo, 35 | Aff, 36 | AffList, 37 | AstBuild, 38 | AstExpr, 39 | AstExprList, 40 | AstNode, 41 | AstNodeList, 42 | AstPrintOptions, 43 | BasicMap, 44 | BasicMapList, 45 | BasicSet, 46 | BasicSetList, 47 | Cell, 48 | Constraint, 49 | ConstraintList, 50 | Context, 51 | Error, 52 | FixedBox, 53 | Flow, 54 | Id, 55 | IdList, 56 | IdToAstExpr, 57 | LocalSpace, 58 | Map, 59 | MapList, 60 | Mat, 61 | MultiAff, 62 | MultiId, 63 | MultiPwAff, 64 | MultiUnionPwAff, 65 | MultiVal, 66 | Point, 67 | Printer, 68 | PwAff, 69 | PwAffList, 70 | PwMultiAff, 71 | PwMultiAffList, 72 | PwQPolynomial, 73 | PwQPolynomialFold, 74 | PwQPolynomialFoldList, 75 | PwQPolynomialList, 76 | QPolynomial, 77 | QPolynomialFold, 78 | QPolynomialList, 79 | Restriction, 80 | Schedule, 81 | ScheduleConstraints, 82 | ScheduleNode, 83 | Set, 84 | SetList, 85 | Space, 86 | StrideInfo, 87 | Term, 88 | UnionAccessInfo, 89 | UnionFlow, 90 | UnionMap, 91 | UnionMapList, 92 | UnionPwAff, 93 | UnionPwAffList, 94 | UnionPwMultiAff, 95 | UnionPwMultiAffList, 96 | UnionPwQPolynomial, 97 | UnionPwQPolynomialFold, 98 | UnionSet, 99 | UnionSetList, 100 | Val, 101 | ValList, 102 | Vec, 103 | Vertex, 104 | Vertices, 105 | ast_expr_op_type, 106 | ast_expr_type, 107 | ast_loop_type, 108 | ast_node_type, 109 | bound, 110 | dim_type, 111 | error, 112 | fold, 113 | format, 114 | isl_version, 115 | on_error, 116 | schedule_algorithm, 117 | schedule_node_type, 118 | stat, 119 | yaml_style, 120 | ) 121 | 122 | # importing _monkeypatch has the side effect of actually monkeypatching 123 | from islpy._monkeypatch import _CHECK_DIM_TYPES, EXPR_CLASSES 124 | 125 | 126 | # }}} 127 | 128 | 129 | # {{{ typing helpers 130 | 131 | Alignable: TypeAlias = ( 132 | Space 133 | | Set | Map 134 | | BasicSet | BasicMap 135 | | Aff | PwAff 136 | ) 137 | AlignableT = TypeVar("AlignableT", bound=Alignable) 138 | AlignableT2 = TypeVar("AlignableT2", bound=Alignable) 139 | 140 | # }}} 141 | 142 | 143 | DEFAULT_CONTEXT = Context() 144 | 145 | 146 | def _get_default_context() -> Context: 147 | """A callable to get the default context for the benefit of Python's 148 | ``__reduce__`` protocol. 149 | """ 150 | return DEFAULT_CONTEXT 151 | 152 | 153 | def _set_dim_id(obj: AlignableT, dt: dim_type, idx: int, id: Id) -> AlignableT: 154 | if isinstance(obj, BasicSet): 155 | s = obj.to_set().set_dim_id(dt, idx, id) 156 | basicsets = s.get_basic_sets() 157 | if not basicsets: 158 | result = BasicSet.empty(s.space) 159 | else: 160 | result, = basicsets 161 | return cast("AlignableT", result) 162 | elif isinstance(obj, BasicMap): 163 | m = obj.to_map().set_dim_id(dt, idx, id) 164 | basicmaps = m.get_basic_maps() 165 | if not basicmaps: 166 | result = BasicMap.empty(m.space) 167 | else: 168 | result, = basicmaps 169 | return cast("AlignableT", result) 170 | 171 | return cast("AlignableT", obj.set_dim_id(dt, idx, id)) 172 | 173 | 174 | def _align_dim_type( 175 | template_dt: dim_type, 176 | obj: AlignableT, 177 | template: Alignable, 178 | obj_bigger_ok: bool, 179 | obj_names: Collection[str], 180 | template_names: Collection[str], 181 | ) -> AlignableT: 182 | 183 | # convert to a type that has get_dim_id 184 | if isinstance(template, BasicSet): 185 | template = template.to_set() 186 | elif isinstance(template, BasicMap): 187 | template = template.to_map() 188 | elif isinstance(template, Aff): 189 | template = template.to_pw_aff() 190 | 191 | # {{{ deal with Aff, PwAff 192 | 193 | # The technique below will not work for PwAff et al, because there is *only* 194 | # the 'param' dim_type, and we are not allowed to move dims around in there. 195 | # We'll make isl do the work, using align_params. 196 | 197 | if template_dt == dim_type.param and isinstance(obj, (Aff, PwAff)): 198 | if not isinstance(template, Space): 199 | template_space = template.space 200 | else: 201 | template_space = template 202 | 203 | if not obj_bigger_ok: 204 | if (obj.dim(template_dt) > template.dim(template_dt) 205 | or not set(obj.get_var_dict()) <= set(template.get_var_dict())): 206 | raise Error("obj has leftover dimensions after alignment") 207 | return obj.align_params(template_space) 208 | 209 | # }}} 210 | 211 | if None in template_names: 212 | all_nones = [None] * len(template_names) 213 | if template_names == all_nones and obj_names == all_nones: 214 | # that's ok 215 | return obj 216 | 217 | raise Error("template may not contain any unnamed dimensions") 218 | 219 | obj_names = set(obj_names) - {None} 220 | template_names = set(template_names) - {None} 221 | 222 | names_in_both = obj_names & template_names 223 | 224 | tgt_idx = 0 225 | while tgt_idx < template.dim(template_dt): 226 | tgt_id = template.get_dim_id(template_dt, tgt_idx) 227 | tgt_name = tgt_id.name 228 | 229 | if tgt_name in names_in_both: 230 | if (obj.dim(template_dt) > tgt_idx 231 | and tgt_name == obj.get_dim_name(template_dt, tgt_idx)): 232 | pass 233 | 234 | else: 235 | src_dt, src_idx = obj.get_var_dict()[tgt_name] 236 | 237 | if src_dt == template_dt: 238 | assert src_idx > tgt_idx 239 | 240 | # isl requires move_dims to be between different types. 241 | # Not sure why. Let's make it happy. 242 | other_dt = dim_type.param 243 | if src_dt == other_dt: 244 | other_dt = dim_type.out 245 | 246 | other_dt_dim = obj.dim(other_dt) 247 | obj = obj.move_dims(other_dt, other_dt_dim, src_dt, src_idx, 1) 248 | obj = obj.move_dims( 249 | template_dt, tgt_idx, other_dt, other_dt_dim, 1) 250 | else: 251 | obj = obj.move_dims(template_dt, tgt_idx, src_dt, src_idx, 1) 252 | 253 | # names are same, make Ids the same, too 254 | obj = _set_dim_id(obj, template_dt, tgt_idx, tgt_id) 255 | 256 | tgt_idx += 1 257 | else: 258 | obj = obj.insert_dims(template_dt, tgt_idx, 1) 259 | obj = _set_dim_id(obj, template_dt, tgt_idx, tgt_id) 260 | 261 | tgt_idx += 1 262 | 263 | if tgt_idx < obj.dim(template_dt) and not obj_bigger_ok: 264 | raise Error("obj has leftover dimensions after alignment") 265 | 266 | return obj 267 | 268 | 269 | def align_spaces( 270 | obj: AlignableT, 271 | template: Alignable, 272 | obj_bigger_ok: bool = False, 273 | ) -> AlignableT: 274 | """ 275 | Try to make the space in which *obj* lives the same as that of *template* by 276 | adding/matching named dimensions. 277 | 278 | :param obj_bigger_ok: If *True*, no error is raised if the resulting *obj* 279 | has more dimensions than *template*. 280 | """ 281 | 282 | have_any_param_domains = ( 283 | isinstance(obj, (Set, BasicSet)) 284 | and isinstance(template, (Set, BasicSet)) 285 | and (obj.is_params() or template.is_params())) 286 | if have_any_param_domains: 287 | if obj.is_params(): 288 | obj = type(obj).from_params(obj) 289 | if template.is_params(): 290 | template = type(template).from_params(template) 291 | 292 | if isinstance(template, EXPR_CLASSES): 293 | dim_types = list(_CHECK_DIM_TYPES) 294 | dim_types.remove(dim_type.out) 295 | else: 296 | dim_types = _CHECK_DIM_TYPES 297 | 298 | obj_names = [ 299 | obj.get_dim_name(dt, i) 300 | for dt in dim_types 301 | for i in range(obj.dim(dt)) 302 | ] 303 | template_names = [ 304 | template.get_dim_name(dt, i) 305 | for dt in dim_types 306 | for i in range(template.dim(dt)) 307 | ] 308 | 309 | for dt in dim_types: 310 | obj = _align_dim_type( 311 | dt, obj, template, obj_bigger_ok, obj_names, template_names) 312 | 313 | return obj 314 | 315 | 316 | def align_two( 317 | obj1: AlignableT, 318 | obj2: AlignableT2, 319 | ) -> tuple[AlignableT, AlignableT2]: 320 | """Align the spaces of two objects, potentially modifying both of them. 321 | 322 | See also :func:`align_spaces`. 323 | """ 324 | 325 | obj1 = align_spaces(obj1, obj2, obj_bigger_ok=True) 326 | obj2 = align_spaces(obj2, obj1, obj_bigger_ok=True) 327 | return (obj1, obj2) 328 | 329 | 330 | def make_zero_and_vars( 331 | set_vars: Sequence[str], 332 | params: Sequence[str] = (), 333 | ctx: Context | None = None 334 | ) -> dict[str | Literal[0], PwAff]: 335 | """ 336 | :arg set_vars: an iterable of variable names, or a comma-separated string 337 | :arg params: an iterable of variable names, or a comma-separated string 338 | 339 | :return: a dictionary from variable names (in *set_vars* and *params*) 340 | to :class:`PwAff` instances that represent each of the 341 | variables. They key '0' is also include and represents 342 | a :class:`PwAff` zero constant. 343 | 344 | .. versionadded:: 2016.1.1 345 | 346 | This function is intended to make it relatively easy to construct sets 347 | programmatically without resorting to string manipulation. 348 | 349 | Usage example:: 350 | 351 | v = isl.make_zero_and_vars("i,j,k", "n") 352 | 353 | myset = ( 354 | v[0].le_set(v["i"] + v["j"]) 355 | & 356 | (v["i"] + v["j"]).lt_set(v["n"]) 357 | & 358 | (v[0].le_set(v["i"])) 359 | & 360 | (v["i"].le_set(13 + v["n"])) 361 | ) 362 | """ 363 | if ctx is None: 364 | ctx = DEFAULT_CONTEXT 365 | 366 | if isinstance(set_vars, str): 367 | set_vars = [s.strip() for s in set_vars.split(",")] 368 | if isinstance(params, str): 369 | params = [s.strip() for s in params.split(",")] 370 | 371 | space = Space.create_from_names(ctx, set=set_vars, params=params) 372 | return affs_from_space(space) 373 | 374 | 375 | def affs_from_space(space: Space) -> dict[Literal[0] | str, PwAff]: 376 | """ 377 | :return: a dictionary from variable names (in *set_vars* and *params*) 378 | to :class:`PwAff` instances that represent each of the 379 | variables *in*space*. They key '0' is also include and represents 380 | a :class:`PwAff` zero constant. 381 | 382 | .. versionadded:: 2016.2 383 | 384 | This function is intended to make it relatively easy to construct sets 385 | programmatically without resorting to string manipulation. 386 | 387 | Usage example:: 388 | 389 | s = isl.Set("[n] -> {[i,j,k]: 0<=i,j,k= 10 and i <= 42)}", 45 | context=ctx) 46 | 47 | points = [] 48 | bset.to_set().foreach_point(points.append) 49 | 50 | for pt in points: 51 | print(pt) 52 | 53 | assert len(points) == 17 54 | 55 | 56 | def test_error_on_invalid_index(): 57 | ctx = isl.Context() 58 | my_set = isl.Set("{ [k, l] : 3l >= -k and 3l <= 10 - k " 59 | "and k >=0 and k <= 2 }", context=ctx) 60 | p = my_set.sample_point() 61 | with pytest.raises(isl.Error): 62 | p.get_coordinate_val(isl.dim_type.set, 99999999) 63 | 64 | 65 | def test_pwqpoly(): 66 | def term_handler(term): 67 | print(term.get_coefficient_val()) 68 | 69 | def piece_handler(set, qpoly): 70 | qpoly.foreach_term(term_handler) 71 | 72 | pwqp = isl.PwQPolynomial("[n] -> { n }") 73 | pwqp.foreach_piece(piece_handler) 74 | 75 | 76 | def no_test_id_user(): 77 | ctx = isl.Context() 78 | foo = isl.Id("foo", context=ctx) # noqa 79 | t = (1, 2) 80 | bar = isl.Id("bar", t, context=ctx) 81 | 82 | assert bar.user is t 83 | 84 | 85 | def test_val(): 86 | for src in [17, "17"]: 87 | v = isl.Val(src) 88 | print(v) 89 | assert v == 17 90 | assert v.to_python() == 17 91 | 92 | 93 | def test_upcast(): 94 | a = isl.PwAff("[n] -> { [(-1 - floor((-n)/4))] }") 95 | b = isl.Aff("[n] -> { [(-1 - floor((-n)/4))] }") 96 | 97 | isl.PwAff(b) 98 | 99 | assert b.is_equal(a) 100 | assert a.is_equal(b) 101 | 102 | s = isl.BasicSet("[n] -> {[i,j,k]: i<=j + k and (exists m: m=j+k) " 103 | "and n mod 5 = 17}") 104 | 105 | isl.UnionSet(s) 106 | 107 | 108 | def test_pickling(): 109 | instances = [ 110 | isl.Aff("[n] -> { [(-1 - floor((-n)/4))] }"), 111 | isl.PwAff("[n] -> { [(0)] : n <= 4 and n >= 1; " 112 | "[(-1 + n - floor((3n)/4))] : n >= 5 }"), 113 | isl.BasicSet("[n] -> {[i,j,k]: i<=j + k and (exists m: m=j+k) " 114 | "and n mod 5 = 17}"), 115 | isl.Set("[n] -> {[i,j,k]: (i<=j + k and (exists m: m=j+k)) or (k=j)}") 116 | ] 117 | 118 | from pickle import dumps, loads 119 | for inst in instances: 120 | inst2 = loads(dumps(inst)) 121 | 122 | assert inst.space == inst2.space 123 | assert inst.is_equal(inst2) 124 | 125 | 126 | def test_apostrophes_during_pickling(): 127 | # Create map and manually insert apostrophes, which are ignored by isl 128 | initial_map = isl.Map( 129 | "[n, m'] -> {[i', j] -> [i] : i = i' + 1 and 0 <= i, i' < n and j = m'}" 130 | ).set_dim_name( 131 | isl.dim_type.in_, 0, "i'", 132 | ).set_dim_name( 133 | isl.dim_type.param, 1, "m'", 134 | ) 135 | 136 | from pickle import dumps, loads 137 | unpickled_map = loads(dumps(initial_map)) 138 | 139 | # Make sure unpickled map still has apostrophes 140 | assert initial_map.get_var_dict() == unpickled_map.get_var_dict() 141 | assert initial_map == unpickled_map 142 | 143 | 144 | def test_get_id_dict(): 145 | id_dict = isl.Set("[a] -> {[b]}").get_id_dict(isl.dim_type.param) 146 | print(id_dict) 147 | assert len(id_dict) == 1 148 | 149 | 150 | def test_get_coefficients_by_name(): 151 | my_set = isl.BasicSet("{ [k, l] : 3l >= -k and 3l <= 10 - k " 152 | "and k >=0 and k <= 2 }") 153 | 154 | for c in my_set.get_constraints(): 155 | print(c.get_coefficients_by_name()) 156 | 157 | assert len(my_set.get_constraints()) == 4 158 | 159 | 160 | def test_count_brick_ish(): 161 | a = isl.BasicSet("[n] -> {[i,j]: 0<= i < n and 0<= j < n and j<= i}") 162 | 163 | def count(set): 164 | if isinstance(set, isl.BasicSet): 165 | set = set.to_set() 166 | result = 1 167 | 168 | for i in range(set.dim(isl.dim_type.set)): 169 | dmax = set.dim_max(i) 170 | dmin = set.dim_min(i) 171 | 172 | length = isl.PwQPolynomial.from_pw_aff(dmax - dmin + 1) 173 | 174 | result = result * length 175 | 176 | return result 177 | 178 | counts = [count(a)] 179 | 180 | if hasattr(a, "card"): 181 | counts.append(a.card()) 182 | 183 | for pwq in counts: 184 | print("EVAL", pwq, "=", pwq.eval_with_dict({"n": 10})) 185 | 186 | print(counts) 187 | 188 | assert counts[0].eval_with_dict({"n": 10}) == 100 189 | if hasattr(a, "card"): 190 | assert counts[1].eval_with_dict({"n": 10}) == 55 191 | 192 | 193 | def test_eval_pw_qpolynomial(): 194 | pwaff = isl.PwAff("[n] -> { [(0)] : n <= 4 and n >= 1; " 195 | "[(-1 + n - floor((3n)/4))] : n >= 5 }") 196 | 197 | pwq = isl.PwQPolynomial.from_pw_aff(pwaff) 198 | 199 | print(pwq.eval_with_dict({"n": 10})) 200 | 201 | assert pwq.eval_with_dict({"n": 10}) == 2 202 | 203 | 204 | def test_schedule(): 205 | schedule = isl.Map("{S[t,i,j] -> [t,i,j]: 0 < t < 20 and 0 < i < j < 100}") 206 | accesses = isl.Map("{S[t,i,j] -> bar[t%2, i+1, j-1]}") 207 | context = isl.Set("{:}") 208 | build = isl.AstBuild.from_context(context) 209 | 210 | def callback(node, build): 211 | schedulemap = build.get_schedule() 212 | accessmap = accesses.apply_domain(schedulemap) 213 | aff = isl.PwMultiAff.from_map(isl.Map.from_union_map(accessmap)) 214 | access = build.call_from_pw_multi_aff(aff) 215 | return isl.AstNode.alloc_user(access) 216 | 217 | build, _callback_handle = build.set_at_each_domain(callback) 218 | 219 | ast = build.ast_from_schedule(schedule) 220 | 221 | def cb_print_user(printer, options, node): 222 | print("Callback user called") 223 | printer = printer.print_str("Callback user") 224 | return printer 225 | 226 | def cb_print_for(printer, options, node): 227 | print("Callback for called") 228 | printer = printer.print_str("Callback For") 229 | return printer 230 | 231 | opts = isl.AstPrintOptions.alloc(isl.DEFAULT_CONTEXT) 232 | opts, _cb_print_user_handle = opts.set_print_user(cb_print_user) 233 | opts, _cb_print_for_handle = opts.set_print_for(cb_print_for) 234 | 235 | printer = isl.Printer.to_str(isl.DEFAULT_CONTEXT) 236 | printer = printer.set_output_format(isl.format.C) 237 | printer.print_str("// Start\n") 238 | printer = ast.print_(printer, opts) 239 | printer.print_str("// End") 240 | 241 | print(printer.get_str()) 242 | 243 | 244 | def test_union_map(): 245 | d = isl.UnionSet("[start, num] -> {S[i,j] : start <= i,j < start + num}") 246 | s = isl.UnionMap("{S[i,j] -> [i,j]}").intersect_domain(d) 247 | aw = isl.UnionMap("{S[i,j] -> B[1024 i + j]}") 248 | aw.compute_flow(aw, aw, s) 249 | 250 | 251 | def test_schedule_dump(): 252 | ctx = isl.Context() 253 | s = isl.UnionSet.read_from_str(ctx, 254 | "{ S_2[i, j, k] : i <= 99 and i >= 0; S_3[i] : " 255 | "i <= 99 and i >= 0; S_0[]; S_1[i] : i <= 99 and i >= 0 }") 256 | cst = isl.ScheduleConstraints.on_domain(s) 257 | schedule = isl.ScheduleConstraints.compute_schedule(cst) 258 | schedule.dump() 259 | 260 | 261 | def test_from_union_map(): 262 | ctx = isl.Context() 263 | m = isl.UnionMap.read_from_str(ctx, 264 | "[m, n] -> { S_0[] -> [0, 0, 0, 0]; S_1[i] -> [i, 1, 0, 0]; S_3[i] -> " 265 | "[1 + i, 3, 0, 0]; S_2[i, j, k] -> [i, 2, j, k] : " 266 | "j <= -1 + m and j >= 0 and k <= -1 + n and k >= 0 }") 267 | 268 | isl.MultiUnionPwAff.from_union_map(m) 269 | 270 | 271 | def test_get_schedule_map(): 272 | ctx = isl.Context() 273 | ss = isl.UnionSet.read_from_str( 274 | ctx, "[m, n] -> { S_2[i, j, k] : " 275 | "j <= -1 + m and j >= 0 and k <= -1 + n and k >= 0 }") 276 | cst1 = isl.ScheduleConstraints.on_domain(ss) 277 | sub_schedule = isl.ScheduleConstraints.compute_schedule(cst1) 278 | sub_schedule.get_map() 279 | 280 | 281 | def test_codegen(): 282 | # courtesy of Marek Pałkowski 283 | 284 | def isl_ast_codegen(S): # noqa: N803 285 | b = isl.AstBuild.from_context(isl.Set("{:}")) 286 | m = isl.Map.from_domain_and_range(S, S) 287 | m = isl.Map.identity(m.get_space()) 288 | m = isl.Map.from_domain(S) 289 | ast = b.ast_from_schedule(m) 290 | p = isl.Printer.to_str(isl.DEFAULT_CONTEXT) 291 | p = p.set_output_format(isl.format.C) 292 | p.flush() 293 | p = p.print_ast_node(ast) 294 | return p.get_str() 295 | 296 | s = isl.Set("[n,m] -> { [i,j] : 0 <= i <= n and i <= j <= m }") 297 | print(isl_ast_codegen(s)) 298 | 299 | 300 | def test_make_zero_and_vars(): 301 | v = isl.make_zero_and_vars("i,j,k", "n") 302 | 303 | myset = ( 304 | v[0].le_set(v["i"] + v["j"]) 305 | & (v["i"] + v["j"]).lt_set(v["n"]) 306 | & (v[0].le_set(v["i"])) 307 | & (v["i"].le_set(13 + v["n"])) 308 | ) 309 | 310 | print(myset) 311 | 312 | 313 | def test_affs_from_space(): 314 | s = isl.Set("[n] -> {[i,j,k]: 0<=i,j,k " 339 | "{ [i0, i1, i2] : 0 <= i0 < n1 and 0 and 0 <= i2 <= 15 }") 340 | 341 | 342 | def test_lexmin(): 343 | print(isl.Set("""{ [s] : exists a,b,c : 344 | 0 <= a <= 5 and 1 <= b <= 4 and 2 <= c <= 7 and 345 | ((2 <= b and b <= 3) implies (a <= 1 or a >= 3)) and 346 | ((not (c < 5 or b > 3)) implies (a > 2 and c < 3)) and s = a + b + c } 347 | """).lexmin()) 348 | 349 | 350 | def test_align_spaces(): 351 | m1 = isl.BasicMap("[m,n] -> {[i,j,k]->[l,o]:}") 352 | m2 = isl.BasicMap("[m,n] -> {[j,k,l,i]->[o]:}") 353 | 354 | result = isl.align_spaces(m1, m2) 355 | assert result.get_var_dict() == m2.get_var_dict() 356 | 357 | a1 = isl.Aff("[t0, t1, t2] -> { [(32)] }") 358 | a2 = isl.Aff("[t1, t0] -> { [(0)] }") 359 | 360 | with pytest.raises(isl.Error): 361 | a1_aligned = isl.align_spaces(a1, a2) 362 | 363 | a1_aligned = isl.align_spaces(a1, a2, obj_bigger_ok=True) 364 | a2_aligned = isl.align_spaces(a2, a1) 365 | 366 | assert a1_aligned == isl.Aff("[t1, t0, t2] -> { [(32)] }") 367 | assert a2_aligned == isl.Aff("[t1, t0, t2] -> { [(0)] }") 368 | 369 | 370 | def test_pass_numpy_int(): 371 | np = pytest.importorskip("numpy") 372 | 373 | s = isl.BasicMap("{[i,j]: 0<=i,j<15}") 374 | c0 = s.get_constraints()[0] 375 | 376 | c1 = c0.set_constant_val(np.int32(5)) 377 | print(c1) 378 | 379 | 380 | def test_isl_align_two(): 381 | a1 = isl.Aff("[t0, t1, t2] -> { [(32)] }") 382 | a2 = isl.Aff("[t1, t0] -> { [(0)] }") 383 | 384 | a1_aligned, a2_aligned = isl.align_two(a1, a2) 385 | assert a1_aligned == isl.Aff("[t1, t0, t2] -> { [(32)] }") 386 | assert a2_aligned == isl.Aff("[t1, t0, t2] -> { [(0)] }") 387 | 388 | b1 = isl.BasicSet("[n0, n1, n2] -> { [i0, i1] : }") 389 | b2 = isl.BasicSet("[n0, n2, n1, n3] -> { [i1, i0, i2] : }") 390 | 391 | b1_aligned, b2_aligned = isl.align_two(b1, b2) 392 | assert b1_aligned == isl.BasicSet("[n0, n2, n1, n3] -> { [i1, i0, i2] : }") 393 | assert b2_aligned == isl.BasicSet("[n0, n2, n1, n3] -> { [i1, i0, i2] : }") 394 | 395 | 396 | def test_bound(): 397 | print(isl.PwQPolynomial("""[n, m] -> {[i, j] -> i * m + j : 398 | 0 <= i < n and 0 <= j < m}""").bound(isl.fold.min)) 399 | print(isl.PwQPolynomial("""[n, m] -> {[i, j] -> i * m + j : 400 | 0 <= i < n and 0 <= j < m}""").bound(isl.fold.max)) 401 | 402 | 403 | def test_copy_context(): 404 | ctx = isl.Context() 405 | import copy 406 | assert not ctx._wraps_same_instance_as(copy.copy(ctx)) 407 | assert not isl.DEFAULT_CONTEXT._wraps_same_instance_as(copy.copy(ctx)) 408 | 409 | 410 | def test_ast_node_list_free(): 411 | # from https://github.com/inducer/islpy/issues/21 412 | # by Cambridge Yang 413 | 414 | ctx = isl.Context() 415 | schedule_map = isl.UnionMap.read_from_str( 416 | ctx, "[N] -> { S0[i] -> [i, 0] : " 417 | "0 <= i < N; S1[i] -> [i, 1] : 0 <= i < N }") 418 | ast_build = isl.AstBuild.from_context(isl.Set.read_from_str(ctx, "[N] -> { : }")) 419 | ast = ast_build.node_from_schedule_map(schedule_map) 420 | 421 | print(ast.to_C_str()) 422 | # Prints below code: 423 | # for (int c0 = 0; c0 < N; c0 += 1) { 424 | # S0(c0); 425 | # S1(c0); 426 | # } 427 | 428 | # we have S0 and S1 in a ast_node_block, which holds "children" of type 429 | # ASTNodeList 430 | body = ast.for_get_body() 431 | assert body.get_type() == isl.ast_node_type.block 432 | 433 | body.block_get_children() 434 | 435 | 436 | def test_union_casts(): 437 | # https://github.com/inducer/islpy/issues/29 438 | s1 = isl.UnionSet("{[0]}") 439 | s2 = isl.BasicSet("{[1]}") 440 | 441 | s2.union(s1) # works fine 442 | s1.union(s2) # did not work while #29 was not fixed 443 | 444 | assert s2.union(s1) == s1.union(s2) 445 | 446 | 447 | def test_remove_map_if_callback(): 448 | ctx = isl.Context() 449 | 450 | umap = isl.UnionMap.read_from_str(ctx, "{A[0] -> [1]; B[1] -> [2]}") 451 | 452 | umap1 = umap.remove_map_if(lambda m: False) 453 | assert umap1 == umap, "map should not change" 454 | 455 | umap2 = umap.remove_map_if(lambda m: 456 | m.get_tuple_name(isl.dim_type.in_) == "B") 457 | assert umap2 == isl.UnionMap.read_from_str(ctx, "{A[0] -> [1]}") 458 | 459 | 460 | def test_remove_map_if_callback_exc(): 461 | pytest.skip("https://github.com/inducer/islpy/pull/33#issuecomment-705165253") 462 | ctx = isl.Context() 463 | 464 | umap = isl.UnionMap.read_from_str(ctx, "{A[0] -> [1]; B[1] -> [2]}") 465 | 466 | def callback_throws_exception(m): 467 | raise AssertionError() 468 | 469 | with pytest.raises(isl.Error): 470 | umap3 = umap.remove_map_if(callback_throws_exception) 471 | del umap3 472 | 473 | 474 | def test_sched_constraints_set_validity(): 475 | domain = isl.UnionSet("[n] -> { A[i] : 0 <= i < n; B[i] : 0 <= i < n }") 476 | validity = isl.UnionMap("[n] -> { A[i] -> B[i] : 0 <= i < n }") 477 | sc = isl.ScheduleConstraints.on_domain(domain) 478 | 479 | sc = sc.set_validity(validity) 480 | validity2 = sc.get_validity() 481 | 482 | print(validity) 483 | print(validity2) 484 | 485 | assert str(validity) == str(validity2) 486 | 487 | 488 | if __name__ == "__main__": 489 | import sys 490 | if len(sys.argv) > 1: 491 | exec(sys.argv[1]) 492 | else: 493 | from pytest import main 494 | main([__file__]) 495 | -------------------------------------------------------------------------------- /.basedpyright/baseline.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "./islpy/__init__.py": [ 4 | { 5 | "code": "reportPrivateUsage", 6 | "range": { 7 | "startColumn": 31, 8 | "endColumn": 47, 9 | "lineCount": 1 10 | } 11 | }, 12 | { 13 | "code": "reportUnusedFunction", 14 | "range": { 15 | "startColumn": 4, 16 | "endColumn": 24, 17 | "lineCount": 1 18 | } 19 | }, 20 | { 21 | "code": "reportReturnType", 22 | "range": { 23 | "startColumn": 15, 24 | "endColumn": 47, 25 | "lineCount": 1 26 | } 27 | }, 28 | { 29 | "code": "reportArgumentType", 30 | "range": { 31 | "startColumn": 34, 32 | "endColumn": 62, 33 | "lineCount": 1 34 | } 35 | }, 36 | { 37 | "code": "reportAssignmentType", 38 | "range": { 39 | "startColumn": 26, 40 | "endColumn": 83, 41 | "lineCount": 1 42 | } 43 | }, 44 | { 45 | "code": "reportAssignmentType", 46 | "range": { 47 | "startColumn": 26, 48 | "endColumn": 76, 49 | "lineCount": 2 50 | } 51 | }, 52 | { 53 | "code": "reportAssignmentType", 54 | "range": { 55 | "startColumn": 26, 56 | "endColumn": 81, 57 | "lineCount": 1 58 | } 59 | }, 60 | { 61 | "code": "reportAssignmentType", 62 | "range": { 63 | "startColumn": 18, 64 | "endColumn": 58, 65 | "lineCount": 1 66 | } 67 | }, 68 | { 69 | "code": "reportUnknownMemberType", 70 | "range": { 71 | "startColumn": 11, 72 | "endColumn": 24, 73 | "lineCount": 1 74 | } 75 | }, 76 | { 77 | "code": "reportAttributeAccessIssue", 78 | "range": { 79 | "startColumn": 15, 80 | "endColumn": 24, 81 | "lineCount": 1 82 | } 83 | }, 84 | { 85 | "code": "reportAttributeAccessIssue", 86 | "range": { 87 | "startColumn": 15, 88 | "endColumn": 24, 89 | "lineCount": 1 90 | } 91 | }, 92 | { 93 | "code": "reportAttributeAccessIssue", 94 | "range": { 95 | "startColumn": 15, 96 | "endColumn": 24, 97 | "lineCount": 1 98 | } 99 | }, 100 | { 101 | "code": "reportAttributeAccessIssue", 102 | "range": { 103 | "startColumn": 15, 104 | "endColumn": 24, 105 | "lineCount": 1 106 | } 107 | }, 108 | { 109 | "code": "reportUnknownVariableType", 110 | "range": { 111 | "startColumn": 12, 112 | "endColumn": 15, 113 | "lineCount": 1 114 | } 115 | }, 116 | { 117 | "code": "reportUnknownMemberType", 118 | "range": { 119 | "startColumn": 18, 120 | "endColumn": 39, 121 | "lineCount": 1 122 | } 123 | }, 124 | { 125 | "code": "reportAssignmentType", 126 | "range": { 127 | "startColumn": 18, 128 | "endColumn": 44, 129 | "lineCount": 1 130 | } 131 | }, 132 | { 133 | "code": "reportAttributeAccessIssue", 134 | "range": { 135 | "startColumn": 28, 136 | "endColumn": 39, 137 | "lineCount": 1 138 | } 139 | }, 140 | { 141 | "code": "reportAttributeAccessIssue", 142 | "range": { 143 | "startColumn": 28, 144 | "endColumn": 39, 145 | "lineCount": 1 146 | } 147 | }, 148 | { 149 | "code": "reportAttributeAccessIssue", 150 | "range": { 151 | "startColumn": 28, 152 | "endColumn": 39, 153 | "lineCount": 1 154 | } 155 | }, 156 | { 157 | "code": "reportAttributeAccessIssue", 158 | "range": { 159 | "startColumn": 28, 160 | "endColumn": 39, 161 | "lineCount": 1 162 | } 163 | }, 164 | { 165 | "code": "reportAttributeAccessIssue", 166 | "range": { 167 | "startColumn": 28, 168 | "endColumn": 39, 169 | "lineCount": 1 170 | } 171 | }, 172 | { 173 | "code": "reportArgumentType", 174 | "range": { 175 | "startColumn": 40, 176 | "endColumn": 43, 177 | "lineCount": 1 178 | } 179 | }, 180 | { 181 | "code": "reportArgumentType", 182 | "range": { 183 | "startColumn": 40, 184 | "endColumn": 43, 185 | "lineCount": 1 186 | } 187 | }, 188 | { 189 | "code": "reportUnknownMemberType", 190 | "range": { 191 | "startColumn": 11, 192 | "endColumn": 29, 193 | "lineCount": 1 194 | } 195 | }, 196 | { 197 | "code": "reportAttributeAccessIssue", 198 | "range": { 199 | "startColumn": 20, 200 | "endColumn": 29, 201 | "lineCount": 1 202 | } 203 | }, 204 | { 205 | "code": "reportAttributeAccessIssue", 206 | "range": { 207 | "startColumn": 20, 208 | "endColumn": 29, 209 | "lineCount": 1 210 | } 211 | }, 212 | { 213 | "code": "reportAttributeAccessIssue", 214 | "range": { 215 | "startColumn": 20, 216 | "endColumn": 29, 217 | "lineCount": 1 218 | } 219 | }, 220 | { 221 | "code": "reportAttributeAccessIssue", 222 | "range": { 223 | "startColumn": 20, 224 | "endColumn": 29, 225 | "lineCount": 1 226 | } 227 | }, 228 | { 229 | "code": "reportUnknownVariableType", 230 | "range": { 231 | "startColumn": 12, 232 | "endColumn": 20, 233 | "lineCount": 1 234 | } 235 | }, 236 | { 237 | "code": "reportUnknownMemberType", 238 | "range": { 239 | "startColumn": 23, 240 | "endColumn": 49, 241 | "lineCount": 1 242 | } 243 | }, 244 | { 245 | "code": "reportAttributeAccessIssue", 246 | "range": { 247 | "startColumn": 38, 248 | "endColumn": 49, 249 | "lineCount": 1 250 | } 251 | }, 252 | { 253 | "code": "reportAttributeAccessIssue", 254 | "range": { 255 | "startColumn": 38, 256 | "endColumn": 49, 257 | "lineCount": 1 258 | } 259 | }, 260 | { 261 | "code": "reportAttributeAccessIssue", 262 | "range": { 263 | "startColumn": 38, 264 | "endColumn": 49, 265 | "lineCount": 1 266 | } 267 | }, 268 | { 269 | "code": "reportAttributeAccessIssue", 270 | "range": { 271 | "startColumn": 38, 272 | "endColumn": 49, 273 | "lineCount": 1 274 | } 275 | }, 276 | { 277 | "code": "reportAttributeAccessIssue", 278 | "range": { 279 | "startColumn": 38, 280 | "endColumn": 49, 281 | "lineCount": 1 282 | } 283 | }, 284 | { 285 | "code": "reportArgumentType", 286 | "range": { 287 | "startColumn": 50, 288 | "endColumn": 58, 289 | "lineCount": 1 290 | } 291 | }, 292 | { 293 | "code": "reportArgumentType", 294 | "range": { 295 | "startColumn": 50, 296 | "endColumn": 58, 297 | "lineCount": 1 298 | } 299 | }, 300 | { 301 | "code": "reportUnknownVariableType", 302 | "range": { 303 | "startColumn": 4, 304 | "endColumn": 18, 305 | "lineCount": 1 306 | } 307 | }, 308 | { 309 | "code": "reportUnknownMemberType", 310 | "range": { 311 | "startColumn": 12, 312 | "endColumn": 33, 313 | "lineCount": 1 314 | } 315 | }, 316 | { 317 | "code": "reportUnknownMemberType", 318 | "range": { 319 | "startColumn": 27, 320 | "endColumn": 39, 321 | "lineCount": 1 322 | } 323 | }, 324 | { 325 | "code": "reportUnknownArgumentType", 326 | "range": { 327 | "startColumn": 27, 328 | "endColumn": 43, 329 | "lineCount": 1 330 | } 331 | }, 332 | { 333 | "code": "reportUnknownArgumentType", 334 | "range": { 335 | "startColumn": 25, 336 | "endColumn": 33, 337 | "lineCount": 1 338 | } 339 | }, 340 | { 341 | "code": "reportArgumentType", 342 | "range": { 343 | "startColumn": 50, 344 | "endColumn": 59, 345 | "lineCount": 1 346 | } 347 | }, 348 | { 349 | "code": "reportArgumentType", 350 | "range": { 351 | "startColumn": 61, 352 | "endColumn": 75, 353 | "lineCount": 1 354 | } 355 | }, 356 | { 357 | "code": "reportUnknownVariableType", 358 | "range": { 359 | "startColumn": 11, 360 | "endColumn": 17, 361 | "lineCount": 1 362 | } 363 | } 364 | ], 365 | "./islpy/_isl.pyi": [ 366 | { 367 | "code": "reportAssignmentType", 368 | "range": { 369 | "startColumn": 13, 370 | "endColumn": 15, 371 | "lineCount": 1 372 | } 373 | }, 374 | { 375 | "code": "reportAssignmentType", 376 | "range": { 377 | "startColumn": 13, 378 | "endColumn": 15, 379 | "lineCount": 1 380 | } 381 | }, 382 | { 383 | "code": "reportOverlappingOverload", 384 | "range": { 385 | "startColumn": 8, 386 | "endColumn": 12, 387 | "lineCount": 1 388 | } 389 | }, 390 | { 391 | "code": "reportOverlappingOverload", 392 | "range": { 393 | "startColumn": 8, 394 | "endColumn": 12, 395 | "lineCount": 1 396 | } 397 | }, 398 | { 399 | "code": "reportOverlappingOverload", 400 | "range": { 401 | "startColumn": 8, 402 | "endColumn": 12, 403 | "lineCount": 1 404 | } 405 | }, 406 | { 407 | "code": "reportIncompatibleMethodOverride", 408 | "range": { 409 | "startColumn": 8, 410 | "endColumn": 14, 411 | "lineCount": 1 412 | } 413 | }, 414 | { 415 | "code": "reportIncompatibleMethodOverride", 416 | "range": { 417 | "startColumn": 8, 418 | "endColumn": 14, 419 | "lineCount": 1 420 | } 421 | }, 422 | { 423 | "code": "reportOverlappingOverload", 424 | "range": { 425 | "startColumn": 8, 426 | "endColumn": 12, 427 | "lineCount": 1 428 | } 429 | }, 430 | { 431 | "code": "reportOverlappingOverload", 432 | "range": { 433 | "startColumn": 8, 434 | "endColumn": 12, 435 | "lineCount": 1 436 | } 437 | }, 438 | { 439 | "code": "reportOverlappingOverload", 440 | "range": { 441 | "startColumn": 8, 442 | "endColumn": 19, 443 | "lineCount": 1 444 | } 445 | }, 446 | { 447 | "code": "reportOverlappingOverload", 448 | "range": { 449 | "startColumn": 8, 450 | "endColumn": 12, 451 | "lineCount": 1 452 | } 453 | }, 454 | { 455 | "code": "reportOverlappingOverload", 456 | "range": { 457 | "startColumn": 8, 458 | "endColumn": 12, 459 | "lineCount": 1 460 | } 461 | }, 462 | { 463 | "code": "reportOverlappingOverload", 464 | "range": { 465 | "startColumn": 8, 466 | "endColumn": 38, 467 | "lineCount": 1 468 | } 469 | }, 470 | { 471 | "code": "reportOverlappingOverload", 472 | "range": { 473 | "startColumn": 8, 474 | "endColumn": 37, 475 | "lineCount": 1 476 | } 477 | }, 478 | { 479 | "code": "reportOverlappingOverload", 480 | "range": { 481 | "startColumn": 8, 482 | "endColumn": 37, 483 | "lineCount": 1 484 | } 485 | }, 486 | { 487 | "code": "reportOverlappingOverload", 488 | "range": { 489 | "startColumn": 8, 490 | "endColumn": 36, 491 | "lineCount": 1 492 | } 493 | }, 494 | { 495 | "code": "reportOverlappingOverload", 496 | "range": { 497 | "startColumn": 8, 498 | "endColumn": 23, 499 | "lineCount": 1 500 | } 501 | }, 502 | { 503 | "code": "reportOverlappingOverload", 504 | "range": { 505 | "startColumn": 8, 506 | "endColumn": 22, 507 | "lineCount": 1 508 | } 509 | }, 510 | { 511 | "code": "reportOverlappingOverload", 512 | "range": { 513 | "startColumn": 8, 514 | "endColumn": 12, 515 | "lineCount": 1 516 | } 517 | }, 518 | { 519 | "code": "reportOverlappingOverload", 520 | "range": { 521 | "startColumn": 8, 522 | "endColumn": 38, 523 | "lineCount": 1 524 | } 525 | }, 526 | { 527 | "code": "reportOverlappingOverload", 528 | "range": { 529 | "startColumn": 8, 530 | "endColumn": 37, 531 | "lineCount": 1 532 | } 533 | }, 534 | { 535 | "code": "reportOverlappingOverload", 536 | "range": { 537 | "startColumn": 8, 538 | "endColumn": 37, 539 | "lineCount": 1 540 | } 541 | }, 542 | { 543 | "code": "reportOverlappingOverload", 544 | "range": { 545 | "startColumn": 8, 546 | "endColumn": 36, 547 | "lineCount": 1 548 | } 549 | }, 550 | { 551 | "code": "reportOverlappingOverload", 552 | "range": { 553 | "startColumn": 8, 554 | "endColumn": 23, 555 | "lineCount": 1 556 | } 557 | }, 558 | { 559 | "code": "reportOverlappingOverload", 560 | "range": { 561 | "startColumn": 8, 562 | "endColumn": 22, 563 | "lineCount": 1 564 | } 565 | }, 566 | { 567 | "code": "reportOverlappingOverload", 568 | "range": { 569 | "startColumn": 8, 570 | "endColumn": 12, 571 | "lineCount": 1 572 | } 573 | } 574 | ], 575 | "./islpy/_monkeypatch.py": [ 576 | { 577 | "code": "reportUnknownParameterType", 578 | "range": { 579 | "startColumn": 4, 580 | "endColumn": 26, 581 | "lineCount": 1 582 | } 583 | }, 584 | { 585 | "code": "reportUnknownParameterType", 586 | "range": { 587 | "startColumn": 27, 588 | "endColumn": 30, 589 | "lineCount": 1 590 | } 591 | }, 592 | { 593 | "code": "reportMissingParameterType", 594 | "range": { 595 | "startColumn": 27, 596 | "endColumn": 30, 597 | "lineCount": 1 598 | } 599 | }, 600 | { 601 | "code": "reportUnknownParameterType", 602 | "range": { 603 | "startColumn": 32, 604 | "endColumn": 39, 605 | "lineCount": 1 606 | } 607 | }, 608 | { 609 | "code": "reportMissingParameterType", 610 | "range": { 611 | "startColumn": 32, 612 | "endColumn": 39, 613 | "lineCount": 1 614 | } 615 | }, 616 | { 617 | "code": "reportUnknownParameterType", 618 | "range": { 619 | "startColumn": 41, 620 | "endColumn": 42, 621 | "lineCount": 1 622 | } 623 | }, 624 | { 625 | "code": "reportMissingParameterType", 626 | "range": { 627 | "startColumn": 41, 628 | "endColumn": 42, 629 | "lineCount": 1 630 | } 631 | }, 632 | { 633 | "code": "reportUnknownParameterType", 634 | "range": { 635 | "startColumn": 44, 636 | "endColumn": 65, 637 | "lineCount": 1 638 | } 639 | }, 640 | { 641 | "code": "reportMissingParameterType", 642 | "range": { 643 | "startColumn": 44, 644 | "endColumn": 65, 645 | "lineCount": 1 646 | } 647 | }, 648 | { 649 | "code": "reportUnknownVariableType", 650 | "range": { 651 | "startColumn": 4, 652 | "endColumn": 16, 653 | "lineCount": 1 654 | } 655 | }, 656 | { 657 | "code": "reportUnknownMemberType", 658 | "range": { 659 | "startColumn": 19, 660 | "endColumn": 36, 661 | "lineCount": 1 662 | } 663 | }, 664 | { 665 | "code": "reportUnknownVariableType", 666 | "range": { 667 | "startColumn": 8, 668 | "endColumn": 16, 669 | "lineCount": 1 670 | } 671 | }, 672 | { 673 | "code": "reportUnknownVariableType", 674 | "range": { 675 | "startColumn": 19, 676 | "endColumn": 21, 677 | "lineCount": 1 678 | } 679 | }, 680 | { 681 | "code": "reportUnknownVariableType", 682 | "range": { 683 | "startColumn": 23, 684 | "endColumn": 30, 685 | "lineCount": 1 686 | } 687 | }, 688 | { 689 | "code": "reportUnknownMemberType", 690 | "range": { 691 | "startColumn": 35, 692 | "endColumn": 62, 693 | "lineCount": 1 694 | } 695 | }, 696 | { 697 | "code": "reportUnknownVariableType", 698 | "range": { 699 | "startColumn": 8, 700 | "endColumn": 20, 701 | "lineCount": 1 702 | } 703 | }, 704 | { 705 | "code": "reportUnknownMemberType", 706 | "range": { 707 | "startColumn": 23, 708 | "endColumn": 48, 709 | "lineCount": 1 710 | } 711 | }, 712 | { 713 | "code": "reportUnknownVariableType", 714 | "range": { 715 | "startColumn": 11, 716 | "endColumn": 23, 717 | "lineCount": 1 718 | } 719 | }, 720 | { 721 | "code": "reportUnknownMemberType", 722 | "range": { 723 | "startColumn": 7, 724 | "endColumn": 35, 725 | "lineCount": 1 726 | } 727 | }, 728 | { 729 | "code": "reportUnknownParameterType", 730 | "range": { 731 | "startColumn": 4, 732 | "endColumn": 18, 733 | "lineCount": 1 734 | } 735 | }, 736 | { 737 | "code": "reportAny", 738 | "range": { 739 | "startColumn": 4, 740 | "endColumn": 7, 741 | "lineCount": 1 742 | } 743 | }, 744 | { 745 | "code": "reportUnknownMemberType", 746 | "range": { 747 | "startColumn": 32, 748 | "endColumn": 47, 749 | "lineCount": 1 750 | } 751 | }, 752 | { 753 | "code": "reportUnknownVariableType", 754 | "range": { 755 | "startColumn": 11, 756 | "endColumn": 64, 757 | "lineCount": 3 758 | } 759 | }, 760 | { 761 | "code": "reportAny", 762 | "range": { 763 | "startColumn": 26, 764 | "endColumn": 37, 765 | "lineCount": 1 766 | } 767 | }, 768 | { 769 | "code": "reportUnknownParameterType", 770 | "range": { 771 | "startColumn": 19, 772 | "endColumn": 23, 773 | "lineCount": 1 774 | } 775 | }, 776 | { 777 | "code": "reportMissingParameterType", 778 | "range": { 779 | "startColumn": 19, 780 | "endColumn": 23, 781 | "lineCount": 1 782 | } 783 | }, 784 | { 785 | "code": "reportUnknownParameterType", 786 | "range": { 787 | "startColumn": 25, 788 | "endColumn": 27, 789 | "lineCount": 1 790 | } 791 | }, 792 | { 793 | "code": "reportMissingParameterType", 794 | "range": { 795 | "startColumn": 25, 796 | "endColumn": 27, 797 | "lineCount": 1 798 | } 799 | }, 800 | { 801 | "code": "reportUnknownParameterType", 802 | "range": { 803 | "startColumn": 29, 804 | "endColumn": 32, 805 | "lineCount": 1 806 | } 807 | }, 808 | { 809 | "code": "reportMissingParameterType", 810 | "range": { 811 | "startColumn": 29, 812 | "endColumn": 32, 813 | "lineCount": 1 814 | } 815 | }, 816 | { 817 | "code": "reportUnnecessaryComparison", 818 | "range": { 819 | "startColumn": 15, 820 | "endColumn": 31, 821 | "lineCount": 1 822 | } 823 | }, 824 | { 825 | "code": "reportUnknownVariableType", 826 | "range": { 827 | "startColumn": 11, 828 | "endColumn": 17, 829 | "lineCount": 1 830 | } 831 | }, 832 | { 833 | "code": "reportUnknownMemberType", 834 | "range": { 835 | "startColumn": 17, 836 | "endColumn": 31, 837 | "lineCount": 1 838 | } 839 | }, 840 | { 841 | "code": "reportUnknownMemberType", 842 | "range": { 843 | "startColumn": 29, 844 | "endColumn": 42, 845 | "lineCount": 1 846 | } 847 | }, 848 | { 849 | "code": "reportUnknownMemberType", 850 | "range": { 851 | "startColumn": 11, 852 | "endColumn": 24, 853 | "lineCount": 1 854 | } 855 | }, 856 | { 857 | "code": "reportUnknownVariableType", 858 | "range": { 859 | "startColumn": 11, 860 | "endColumn": 31, 861 | "lineCount": 1 862 | } 863 | }, 864 | { 865 | "code": "reportUnknownMemberType", 866 | "range": { 867 | "startColumn": 51, 868 | "endColumn": 65, 869 | "lineCount": 1 870 | } 871 | } 872 | ] 873 | } 874 | } -------------------------------------------------------------------------------- /islpy/_monkeypatch.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from collections.abc import Callable, Collection, Iterable, Mapping, Sequence 4 | from functools import update_wrapper 5 | from sys import intern 6 | from typing import ( 7 | TYPE_CHECKING, 8 | ClassVar, 9 | Concatenate, 10 | Literal, 11 | ParamSpec, 12 | Protocol, 13 | TypeAlias, 14 | TypeVar, 15 | cast, 16 | ) 17 | from warnings import warn 18 | 19 | 20 | if TYPE_CHECKING: 21 | import islpy._isl as _isl 22 | else: 23 | import sys 24 | if "_isl" not in sys.modules: 25 | import islpy._isl as _isl 26 | else: 27 | # This is used for monkeypatching during stub generation. 28 | # See stubgen/stubgen.py and CMakeLists for orchestration details. 29 | import _isl 30 | 31 | 32 | ALL_CLASSES: tuple[type, ...] = tuple( 33 | getattr(_isl, cls) for cls in dir(_isl) if cls[0].isupper()) 34 | EXPR_CLASSES: tuple[type, ...] = tuple(cls for cls in ALL_CLASSES 35 | if "Aff" in cls.__name__ or "Polynomial" in cls.__name__) 36 | ARITH_CLASSES: tuple[type, ...] = ( 37 | _isl.Aff, _isl.PwAff, _isl.QPolynomial, _isl.PwQPolynomial) 38 | 39 | _CHECK_DIM_TYPES: tuple[_isl.dim_type, ...] = ( 40 | _isl.dim_type.in_, _isl.dim_type.param, _isl.dim_type.set) 41 | 42 | 43 | # {{{ typing helpers 44 | 45 | T = TypeVar("T") 46 | P = ParamSpec("P") 47 | ResultT = TypeVar("ResultT") 48 | 49 | SelfT = TypeVar("SelfT") 50 | 51 | BasicT = TypeVar("BasicT", _isl.BasicSet, _isl.BasicMap) 52 | 53 | AffOrConstraintT = TypeVar("AffOrConstraintT", _isl.Aff, _isl.Constraint) 54 | AffLikeT = TypeVar("AffLikeT", _isl.Aff, _isl.PwAff) 55 | ExprLike: TypeAlias = _isl.Aff | _isl.PwAff | _isl.QPolynomial | _isl.PwQPolynomial 56 | ExprLikeT = TypeVar("ExprLikeT", _isl.Aff, _isl.PwAff, 57 | _isl.QPolynomial, _isl.PwQPolynomial 58 | ) 59 | SetLikeT = TypeVar("SetLikeT", bound=_isl.BasicSet | _isl.Set) 60 | 61 | SetOrBasic: TypeAlias = _isl.BasicSet | _isl.Set 62 | SetOrBasicT = TypeVar("SetOrBasicT", bound=SetOrBasic) 63 | 64 | MapOrBasic: TypeAlias = _isl.BasicMap | _isl.Map 65 | MapOrBasicT = TypeVar("MapOrBasicT", bound=MapOrBasic) 66 | 67 | SetOrMap: TypeAlias = _isl.BasicSet | _isl.Set | _isl.BasicMap | _isl.Map 68 | SetOrMapT = TypeVar("SetOrMapT", _isl.BasicSet, _isl.Set, _isl.BasicMap, _isl.Map) 69 | 70 | HasSpace: TypeAlias = ( 71 | _isl.Space 72 | | _isl.Constraint 73 | | _isl.LocalSpace 74 | | _isl.Aff 75 | | _isl.MultiAff 76 | | _isl.PwAff 77 | | _isl.PwMultiAff 78 | | _isl.BasicMap 79 | | _isl.BasicSet 80 | | _isl.Set 81 | | _isl.Map 82 | ) 83 | 84 | 85 | class IslObject(Protocol): 86 | def get_ctx(self) -> _isl.Context: 87 | ... 88 | 89 | def _wraps_same_instance_as(self, other: object) -> bool: 90 | ... 91 | 92 | _base_name: ClassVar[str] 93 | 94 | # }}} 95 | 96 | 97 | # {{{ copied verbatim from pytools to avoid numpy/pytools dependency 98 | 99 | class _HasKwargs: 100 | pass 101 | 102 | 103 | def _memoize_on_first_arg( 104 | function: Callable[Concatenate[T, P], ResultT], *, 105 | cache_dict_name: str | None = None) -> Callable[Concatenate[T, P], ResultT]: 106 | """Like :func:`memoize_method`, but for functions that take the object 107 | in which do memoization information is stored as first argument. 108 | 109 | Supports cache deletion via ``function_name.clear_cache(self)``. 110 | """ 111 | 112 | if cache_dict_name is None: 113 | cache_dict_name = intern( 114 | f"_memoize_dic_{function.__module__}{function.__name__}" 115 | ) 116 | 117 | def wrapper(obj: T, *args: P.args, **kwargs: P.kwargs) -> ResultT: 118 | key = (_HasKwargs, frozenset(kwargs.items()), *args) if kwargs else args 119 | 120 | assert cache_dict_name is not None 121 | try: 122 | return cast("ResultT", getattr(obj, cache_dict_name)[key]) 123 | except AttributeError: 124 | attribute_error = True 125 | except KeyError: 126 | attribute_error = False 127 | 128 | result = function(obj, *args, **kwargs) 129 | if attribute_error: 130 | object.__setattr__(obj, cache_dict_name, {key: result}) 131 | return result 132 | getattr(obj, cache_dict_name)[key] = result 133 | return result 134 | 135 | def clear_cache(obj: object): 136 | object.__delattr__(obj, cache_dict_name) 137 | 138 | from functools import update_wrapper 139 | new_wrapper = update_wrapper(wrapper, function) 140 | 141 | # type-ignore because mypy has a point here, stuffing random attributes 142 | # into the function's dict is moderately sketchy. 143 | new_wrapper.clear_cache = clear_cache # type: ignore[attr-defined] 144 | 145 | return new_wrapper 146 | 147 | 148 | # }}} 149 | 150 | 151 | def _read_from_str_wrapper(cls, context, s, dims_with_apostrophes): 152 | """A callable to reconstitute instances from strings for the benefit 153 | of Python's ``__reduce__`` protocol. 154 | """ 155 | cls_from_str = cls.read_from_str(context, s) 156 | 157 | # Apostrophes in dim names have been lost, put them back 158 | for dim_name, (dt, dim_idx) in dims_with_apostrophes.items(): 159 | cls_from_str = cls_from_str.set_dim_name(dt, dim_idx, dim_name) 160 | 161 | return cls_from_str 162 | 163 | 164 | def dim_type_reduce(self: _isl.dim_type): 165 | return (_isl.dim_type, (int(self),)) 166 | 167 | 168 | def context_reduce(self: _isl.Context): 169 | from islpy import DEFAULT_CONTEXT, _get_default_context 170 | if self._wraps_same_instance_as(DEFAULT_CONTEXT): 171 | return (_get_default_context, ()) 172 | else: 173 | return (_isl.Context, ()) 174 | 175 | 176 | def context_eq(self: IslObject, other: object): 177 | return isinstance(other, _isl.Context) and self._wraps_same_instance_as(other) 178 | 179 | 180 | def context_ne(self: object, other: object) -> bool: 181 | return not self.__eq__(other) 182 | 183 | 184 | def generic_reduce(self: HasSpace): 185 | ctx = self.get_ctx() 186 | prn = _isl.Printer.to_str(ctx) 187 | prn = getattr(prn, f"print_{self._base_name}")(self) 188 | 189 | # Reconstructing from string will remove apostrophes in dim names, 190 | # so keep track of dim names with apostrophes 191 | dims_with_apostrophes = { 192 | dname: pos for dname, pos in self.get_var_dict().items() 193 | if "'" in dname} 194 | 195 | return ( 196 | _read_from_str_wrapper, 197 | (type(self), ctx, prn.get_str(), dims_with_apostrophes)) 198 | 199 | 200 | def generic_str(self: IslObject) -> str: 201 | prn = _isl.Printer.to_str(self.get_ctx()) 202 | getattr(prn, f"print_{self._base_name}")(self) 203 | return prn.get_str() 204 | 205 | 206 | def generic_repr(self: IslObject) -> str: 207 | prn = _isl.Printer.to_str(self.get_ctx()) 208 | getattr(prn, f"print_{self._base_name}")(self) 209 | return f'{type(self).__name__}("{prn.get_str()}")' 210 | 211 | 212 | def space_get_id_dict( 213 | self: _isl.Space, 214 | dimtype: _isl.dim_type | None = None 215 | ) -> Mapping[_isl.Id, tuple[_isl.dim_type, int]]: 216 | """Return a dictionary mapping variable :class:`Id` instances to tuples 217 | of (:class:`dim_type`, index). 218 | 219 | :param dimtype: None to get all variables, otherwise 220 | one of :class:`dim_type`. 221 | """ 222 | result = {} 223 | 224 | def set_dim_id(name, tp, idx): 225 | if name in result: 226 | raise RuntimeError(f"non-unique var id '{name}' encountered") 227 | result[name] = tp, idx 228 | 229 | if dimtype is None: 230 | types = _CHECK_DIM_TYPES 231 | else: 232 | types = [dimtype] 233 | 234 | for tp in types: 235 | for i in range(self.dim(tp)): 236 | name = self.get_dim_id(tp, i) 237 | if name is not None: 238 | set_dim_id(name, tp, i) 239 | 240 | return result 241 | 242 | 243 | def space_get_var_dict( 244 | self: _isl.Space, 245 | dimtype: _isl.dim_type | None = None, 246 | ignore_out: bool = False 247 | ) -> Mapping[str, tuple[_isl.dim_type, int]]: 248 | """Return a dictionary mapping variable names to tuples of 249 | (:class:`dim_type`, index). 250 | 251 | :param dimtype: None to get all variables, otherwise 252 | one of :class:`dim_type`. 253 | """ 254 | result: dict[str, tuple[_isl.dim_type, int]] = {} 255 | 256 | def set_dim_name(name: str, tp: _isl.dim_type, idx: int): 257 | if name in result: 258 | raise RuntimeError(f"non-unique var name '{name}' encountered") 259 | result[name] = tp, idx 260 | 261 | if dimtype is None: 262 | types = list(_CHECK_DIM_TYPES) 263 | if ignore_out: 264 | types = types[:] 265 | types.remove(_isl.dim_type.out) 266 | else: 267 | types = [dimtype] 268 | 269 | for tp in types: 270 | for i in range(self.dim(tp)): 271 | name = self.get_dim_name(tp, i) 272 | if name is not None: 273 | set_dim_name(name, tp, i) 274 | 275 | return result 276 | 277 | 278 | def space_create_from_names( 279 | ctx: _isl.Context, 280 | set: Sequence[str] | None = None, 281 | in_: Sequence[str] | None = None, 282 | out: Sequence[str] | None = None, 283 | params: Sequence[str] = () 284 | ) -> _isl.Space: 285 | """Create a :class:`Space` from lists of variable names. 286 | 287 | :param set_: names of `set`-type variables. 288 | :param in_: names of `in`-type variables. 289 | :param out: names of `out`-type variables. 290 | :param params: names of parameter-type variables. 291 | """ 292 | dt = _isl.dim_type 293 | 294 | if set is not None: 295 | if in_ is not None or out is not None: 296 | raise RuntimeError("must pass only one of set / (in_,out)") 297 | 298 | result = _isl.Space.set_alloc(ctx, nparam=len(params), 299 | dim=len(set)) 300 | 301 | for i, name in enumerate(set): 302 | result = result.set_dim_name(dt.set, i, name) 303 | 304 | elif in_ is not None and out is not None: 305 | if set is not None: 306 | raise RuntimeError("must pass only one of set / (in_,out)") 307 | 308 | result = _isl.Space.alloc(ctx, nparam=len(params), 309 | n_in=len(in_), n_out=len(out)) 310 | 311 | for i, name in enumerate(in_): 312 | result = result.set_dim_name(dt.in_, i, name) 313 | 314 | for i, name in enumerate(out): 315 | result = result.set_dim_name(dt.out, i, name) 316 | else: 317 | raise RuntimeError("invalid parameter combination") 318 | 319 | for i, name in enumerate(params): 320 | result = result.set_dim_name(dt.param, i, name) 321 | 322 | return result 323 | 324 | 325 | def set_or( 326 | self: _isl.Set, 327 | other: _isl.Set | _isl.BasicSet, 328 | ) -> _isl.Set: 329 | try: 330 | return self.union(other) 331 | except TypeError: 332 | return NotImplemented 333 | 334 | 335 | def bset_and( 336 | self: _isl.BasicSet, 337 | other: SetOrBasicT, 338 | ) -> SetOrBasicT: 339 | if isinstance(other, _isl.Set): 340 | try: 341 | return self.to_set().intersect(other) 342 | except TypeError: 343 | return NotImplemented 344 | else: 345 | try: 346 | return self.intersect(other) 347 | except TypeError: 348 | return NotImplemented 349 | 350 | 351 | def set_and( 352 | self: _isl.Set, 353 | other: _isl.Set | _isl.BasicSet, 354 | ) -> _isl.Set: 355 | if isinstance(self, _isl.BasicSet): 356 | self = self.to_set() 357 | try: 358 | return self.intersect(other) 359 | except TypeError: 360 | return NotImplemented 361 | 362 | 363 | def set_sub( 364 | self: _isl.Set | _isl.BasicSet, 365 | other: _isl.Set | _isl.BasicSet, 366 | ) -> _isl.Set: 367 | if isinstance(self, _isl.BasicSet): 368 | self = self.to_set() 369 | try: 370 | return self.subtract(other) 371 | except TypeError: 372 | return NotImplemented 373 | 374 | 375 | def map_or( 376 | self: _isl.Map | _isl.BasicMap, 377 | other: _isl.Map | _isl.BasicMap, 378 | ) -> _isl.Map: 379 | if isinstance(self, _isl.BasicMap): 380 | self = self.to_map() 381 | try: 382 | return self.union(other) 383 | except TypeError: 384 | return NotImplemented 385 | 386 | 387 | def bmap_and( 388 | self: _isl.BasicMap, 389 | other: MapOrBasicT, 390 | ) -> MapOrBasicT: 391 | if isinstance(other, _isl.Map): 392 | try: 393 | return self.to_map().intersect(other) 394 | except TypeError: 395 | return NotImplemented 396 | else: 397 | try: 398 | return self.intersect(other) 399 | except TypeError: 400 | return NotImplemented 401 | 402 | 403 | def map_and( 404 | self: _isl.Map, 405 | other: _isl.Map | _isl.BasicMap, 406 | ) -> _isl.Map: 407 | try: 408 | return self.intersect(other) 409 | except TypeError: 410 | return NotImplemented 411 | 412 | 413 | def map_sub( 414 | self: _isl.Map | _isl.BasicMap, 415 | other: _isl.Map | _isl.BasicMap, 416 | ) -> _isl.Map: 417 | if isinstance(self, _isl.BasicMap): 418 | self = self.to_map() 419 | try: 420 | return self.subtract(other) 421 | except TypeError: 422 | return NotImplemented 423 | 424 | 425 | def obj_set_coefficients( 426 | self: AffOrConstraintT, 427 | dim_tp: _isl.dim_type, 428 | args: Sequence[_isl.Val | int], 429 | ) -> AffOrConstraintT: 430 | """ 431 | :param dim_tp: :class:`dim_type` 432 | :param args: :class:`list` of coefficients, for indices `0..len(args)-1`. 433 | 434 | .. versionchanged:: 2011.3 435 | New for :class:`Aff` 436 | """ 437 | for i, coeff in enumerate(args): 438 | self = self.set_coefficient_val(dim_tp, i, coeff) 439 | 440 | return self 441 | 442 | 443 | def obj_set_coefficients_by_name( 444 | self: AffOrConstraintT, 445 | iterable: Iterable[tuple[str | Literal[1], _isl.Val | int]] 446 | | Mapping[str | Literal[1], _isl.Val | int], 447 | name_to_dim: Mapping[str, tuple[_isl.dim_type, int]] | None = None, 448 | ) -> AffOrConstraintT: 449 | """Set the coefficients and the constant. 450 | 451 | :param iterable: a :class:`dict` or iterable of :class:`tuple` 452 | instances mapping variable names to their coefficients. 453 | The constant is set to the value of the key '1'. 454 | 455 | .. versionchanged:: 2011.3 456 | New for :class:`Aff` 457 | """ 458 | try: 459 | coeff_iterable: Iterable[tuple[str | Literal[1], _isl.Val | int]] = \ 460 | list(iterable.items()) 461 | except AttributeError: 462 | coeff_iterable = \ 463 | cast("Iterable[tuple[str | Literal[1], _isl.Val | int]]", iterable) 464 | 465 | if name_to_dim is None: 466 | name_to_dim = obj_get_var_dict(self) 467 | 468 | for name, coeff in coeff_iterable: 469 | if name == 1: 470 | self = self.set_constant_val(coeff) 471 | else: 472 | assert name 473 | tp, idx = name_to_dim[name] 474 | self = self.set_coefficient_val(tp, idx, coeff) 475 | 476 | return self 477 | 478 | 479 | def obj_get_coefficients_by_name( 480 | self: _isl.Constraint | _isl.Aff, 481 | dimtype: _isl.dim_type | None = None, 482 | dim_to_name: Mapping[tuple[_isl.dim_type, int], str] | None = None, 483 | ) -> dict[str | Literal[1], _isl.Val]: 484 | """Return a dictionary mapping variable names to coefficients. 485 | 486 | :param dimtype: None to get all variables, otherwise 487 | one of :class:`dim_type`. 488 | 489 | .. versionchanged:: 2011.3 490 | New for :class:`Aff` 491 | """ 492 | if dimtype is None: 493 | types: Sequence[_isl.dim_type] = _CHECK_DIM_TYPES 494 | else: 495 | types = [dimtype] 496 | 497 | result: dict[Literal[1] | str, _isl.Val] = {} 498 | for tp in types: 499 | for i in range(self.get_space().dim(tp)): 500 | coeff = self.get_coefficient_val(tp, i) 501 | if coeff: 502 | if dim_to_name is None: 503 | name = self.get_dim_name(tp, i) 504 | assert name 505 | else: 506 | name = dim_to_name[tp, i] 507 | 508 | result[name] = coeff 509 | 510 | const = self.get_constant_val() 511 | if const: 512 | result[1] = const 513 | 514 | return result 515 | 516 | 517 | def eq_from_names( 518 | space: _isl.Space, 519 | coefficients: Mapping[str | Literal[1], _isl.Val | int] | None = None 520 | ) -> _isl.Constraint: 521 | """Create a constraint `const + coeff_1*var_1 +... == 0`. 522 | 523 | :param space: :class:`Space` 524 | :param coefficients: a :class:`dict` or iterable of :class:`tuple` 525 | instances mapping variable names to their coefficients 526 | The constant is set to the value of the key '1'. 527 | 528 | .. versionchanged:: 2011.3 529 | Eliminated the separate *const* parameter. 530 | """ 531 | if coefficients is None: 532 | coefficients = {} 533 | c = _isl.Constraint.equality_alloc(space) 534 | return obj_set_coefficients_by_name(c, coefficients) 535 | 536 | 537 | def ineq_from_names( 538 | space: _isl.Space, 539 | coefficients: Mapping[str | Literal[1], _isl.Val | int] | None = None 540 | ) -> _isl.Constraint: 541 | """Create a constraint `const + coeff_1*var_1 +... >= 0`. 542 | 543 | :param space: :class:`Space` 544 | :param coefficients: a :class:`dict` or iterable of :class:`tuple` 545 | instances mapping variable names to their coefficients 546 | The constant is set to the value of the key '1'. 547 | 548 | .. versionchanged:: 2011.3 549 | Eliminated the separate *const* parameter. 550 | """ 551 | if coefficients is None: 552 | coefficients = {} 553 | c = _isl.Constraint.inequality_alloc(space) 554 | return obj_set_coefficients_by_name(c, coefficients) 555 | 556 | 557 | def basic_obj_get_constraints( 558 | self: _isl.BasicSet | _isl.BasicMap 559 | ) -> list[_isl.Constraint]: 560 | """Get a list of constraints.""" 561 | result: list[_isl.Constraint] = [] 562 | self.foreach_constraint(result.append) 563 | return result 564 | 565 | 566 | def set_get_basic_sets(self: _isl.Set | _isl.BasicSet) -> list[_isl.BasicSet]: 567 | """Get the list of :class:`BasicSet` instances in this :class:`Set`.""" 568 | result: list[_isl.BasicSet] = [] 569 | if isinstance(self, _isl.BasicSet): 570 | self = self.to_set() 571 | self.foreach_basic_set(result.append) 572 | return result 573 | 574 | 575 | def map_get_basic_maps(self: _isl.Map) -> list[_isl.BasicMap]: 576 | """Get the list of :class:`BasicMap` instances in this :class:`Map`.""" 577 | result: list[_isl.BasicMap] = [] 578 | self.foreach_basic_map(result.append) 579 | return result 580 | 581 | 582 | def obj_get_id_dict( 583 | self: HasSpace, 584 | dimtype: _isl.dim_type | None = None 585 | ) -> Mapping[_isl.Id, tuple[_isl.dim_type, int]]: 586 | """Return a dictionary mapping :class:`Id` instances to tuples of 587 | (:class:`dim_type`, index). 588 | 589 | :param dimtype: None to get all variables, otherwise 590 | one of :class:`dim_type`. 591 | """ 592 | return self.get_space().get_id_dict(dimtype) 593 | 594 | 595 | @_memoize_on_first_arg 596 | def obj_get_var_dict( 597 | self: HasSpace, 598 | dimtype: _isl.dim_type | None = None 599 | ) -> Mapping[str, tuple[_isl.dim_type, int]]: 600 | """Return a dictionary mapping variable names to tuples of 601 | (:class:`dim_type`, index). 602 | 603 | :param dimtype: None to get all variables, otherwise 604 | one of :class:`dim_type`. 605 | """ 606 | return self.get_space().get_var_dict( 607 | dimtype, ignore_out=isinstance(self, EXPR_CLASSES)) 608 | 609 | 610 | def obj_get_var_ids( 611 | self: HasSpace, 612 | dimtype: _isl.dim_type 613 | ) -> Sequence[str | None]: 614 | """Return a list of :class:`Id` instances for :class:`dim_type` *dimtype*.""" 615 | return [ 616 | self.get_dim_name(dimtype, i) 617 | for i in range(self.dim(dimtype))] 618 | 619 | 620 | @_memoize_on_first_arg 621 | def obj_get_var_names_not_none( 622 | self: HasSpace, 623 | dimtype: _isl.dim_type, 624 | ) -> Sequence[str]: 625 | """Return a list of dim names (in order) for :class:`dim_type` *dimtype*. 626 | 627 | Raise :exc:`ValueError` if any of the names is *None*. 628 | 629 | .. versionadded:: 2025.2.5 630 | """ 631 | ndim = self.dim(dimtype) 632 | res = [n 633 | for i in range(ndim) 634 | if (n := self.get_dim_name(dimtype, i)) is not None] 635 | if len(res) != ndim: 636 | raise ValueError("None encountered in dim names") 637 | return res 638 | 639 | 640 | @_memoize_on_first_arg 641 | def obj_get_var_names( 642 | self: HasSpace, 643 | dimtype: _isl.dim_type, 644 | ) -> Sequence[str | None]: 645 | """Return a list of dim names (in order) for :class:`dim_type` *dimtype*. 646 | """ 647 | return [self.get_dim_name(dimtype, i) 648 | for i in range(self.dim(dimtype))] 649 | 650 | 651 | def pwaff_get_pieces(self: _isl.PwAff | _isl.Aff) -> list[tuple[_isl.Set, _isl.Aff]]: 652 | if isinstance(self, _isl.Aff): 653 | self = self.to_pw_aff() 654 | result: list[tuple[_isl.Set, _isl.Aff]] = [] 655 | 656 | def append_tuple(s: _isl.Set, v: _isl.Aff): 657 | result.append((s, v)) 658 | 659 | self.foreach_piece(append_tuple) 660 | return result 661 | 662 | 663 | def pwqpolynomial_get_pieces( 664 | self: _isl.PwQPolynomial 665 | ) -> list[tuple[_isl.Set, _isl.QPolynomial]]: 666 | """ 667 | :return: list of (:class:`Set`, :class:`QPolynomial`) 668 | """ 669 | 670 | result: list[tuple[_isl.Set, _isl.QPolynomial]] = [] 671 | 672 | def append_tuple(s: _isl.Set, v: _isl.QPolynomial): 673 | result.append((s, v)) 674 | 675 | self.foreach_piece(append_tuple) 676 | return result 677 | 678 | 679 | def pw_get_aggregate_domain(self: _isl.PwAff | _isl.PwQPolynomial) -> _isl.Set: 680 | """ 681 | :return: a :class:`Set` that is the union of the domains of all pieces 682 | """ 683 | 684 | result = _isl.Set.empty(self.get_domain_space()) 685 | for dom, _ in self.get_pieces(): 686 | result = result.union(dom) 687 | 688 | return result 689 | 690 | 691 | def qpolynomial_get_terms(self: _isl.QPolynomial) -> list[_isl.Term]: 692 | """Get the list of :class:`Term` instances in this :class:`QPolynomial`.""" 693 | result: list[_isl.Term] = [] 694 | self.foreach_term(result.append) 695 | return result 696 | 697 | 698 | def pwqpolynomial_eval_with_dict( 699 | self: _isl.PwQPolynomial, 700 | value_dict: Mapping[str, int | _isl.Val] 701 | ) -> int: 702 | """Evaluates *self* for the parameters specified by 703 | *value_dict*, which maps parameter names to their values. 704 | """ 705 | 706 | pt = _isl.Point.zero(self.space.params()) 707 | 708 | for i in range(self.space.dim(_isl.dim_type.param)): 709 | par_name = self.space.get_dim_name(_isl.dim_type.param, i) 710 | assert par_name 711 | pt = pt.set_coordinate_val( 712 | _isl.dim_type.param, i, value_dict[par_name]) 713 | 714 | return self.eval(pt).to_python() 715 | 716 | 717 | def _number_to_expr_like(template: ExprLikeT, num: int | _isl.Val) -> ExprLikeT: 718 | number_aff = _isl.Aff.zero_on_domain(template.get_domain_space()) 719 | number_aff = number_aff.set_constant_val(num) 720 | 721 | if isinstance(template, _isl.Aff): 722 | return number_aff 723 | if isinstance(template, _isl.QPolynomial): 724 | return _isl.QPolynomial.from_aff(number_aff) 725 | 726 | # everything else is piecewise 727 | 728 | if template.get_pieces(): 729 | number_pw_aff = _isl.PwAff.empty(template.get_space()) 730 | for set, _ in template.get_pieces(): 731 | number_pw_aff = set.indicator_function().cond( 732 | number_aff, number_pw_aff) 733 | else: 734 | number_pw_aff = _isl.PwAff.alloc( 735 | _isl.Set.universe(template.domain().space), 736 | number_aff) 737 | 738 | if isinstance(template, _isl.PwAff): 739 | return number_pw_aff 740 | 741 | elif isinstance(template, _isl.PwQPolynomial): 742 | return _isl.PwQPolynomial.from_pw_aff(number_pw_aff) 743 | 744 | else: 745 | raise TypeError("unexpected template type") 746 | 747 | 748 | def expr_like_add(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT: 749 | if not isinstance(other, ExprLike): 750 | other = _number_to_expr_like(self, other) 751 | 752 | try: 753 | return self.add(other) 754 | except TypeError: 755 | return NotImplemented 756 | 757 | 758 | def expr_like_sub(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT: 759 | if not isinstance(other, ExprLike): 760 | other = _number_to_expr_like(self, other) 761 | 762 | try: 763 | return self.sub(other) 764 | except TypeError: 765 | return NotImplemented 766 | 767 | 768 | def expr_like_rsub(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT: 769 | if not isinstance(other, ExprLike): 770 | other = _number_to_expr_like(self, other) 771 | 772 | return -self + other 773 | 774 | 775 | def expr_like_mul(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT: 776 | if not isinstance(other, ExprLike): 777 | other = _number_to_expr_like(self, other) 778 | 779 | try: 780 | return self.mul(other) 781 | except TypeError: 782 | return NotImplemented 783 | 784 | 785 | def expr_like_floordiv(self: AffLikeT, other: _isl.Val) -> AffLikeT: 786 | return self.scale_down_val(other).floor() 787 | 788 | 789 | def val_rsub(self: _isl.Val, other: _isl.Val) -> _isl.Val: 790 | return -self + other 791 | 792 | 793 | def val_bool(self: _isl.Val) -> bool: 794 | return not self.is_zero() 795 | 796 | 797 | def val_repr(self: _isl.Val) -> str: 798 | return f'{type(self).__name__}("{self.to_str()}")' 799 | 800 | 801 | def val_to_python(self: _isl.Val) -> int: 802 | if not self.is_int(): 803 | raise ValueError("can only convert integer Val to python") 804 | 805 | return int(self.to_str()) 806 | 807 | 808 | def obj_eq(self: IslObject, other: object) -> bool: 809 | assert self.get_ctx() == other.get_ctx(), ( 810 | "Equality-comparing two objects from different ISL Contexts " 811 | "will likely lead to entertaining (but never useful) results. " 812 | "In particular, Spaces with matching names will no longer be " 813 | "equal.") 814 | 815 | return self.is_equal(other) 816 | 817 | 818 | def obj_ne(self: object, other: object) -> bool: 819 | return not self.__eq__(other) 820 | 821 | 822 | for cls in ALL_CLASSES: 823 | if hasattr(cls, "is_equal"): 824 | cls.__eq__ = obj_eq 825 | cls.__ne__ = obj_ne 826 | 827 | 828 | def set_lt(self: _isl.BasicSet | _isl.Set, other: _isl.BasicSet | _isl.Set) -> bool: 829 | return self.is_strict_subset(other) 830 | 831 | 832 | def set_le(self: _isl.BasicSet | _isl.Set, other: _isl.BasicSet | _isl.Set) -> bool: 833 | return self.is_subset(other) 834 | 835 | 836 | def set_gt(self: _isl.BasicSet | _isl.Set, other: _isl.BasicSet | _isl.Set) -> bool: 837 | return other.is_strict_subset(self) 838 | 839 | 840 | def set_ge(self: _isl.BasicSet | _isl.Set, other: _isl.BasicSet | _isl.Set) -> bool: 841 | return other.is_subset(self) 842 | 843 | 844 | def map_lt(self: _isl.BasicMap | _isl.Map, other: _isl.BasicMap | _isl.Map) -> bool: 845 | return self.is_strict_subset(other) 846 | 847 | 848 | def map_le(self: _isl.BasicMap | _isl.Map, other: _isl.BasicMap | _isl.Map) -> bool: 849 | return self.is_subset(other) 850 | 851 | 852 | def map_gt(self: _isl.BasicMap | _isl.Map, other: _isl.BasicMap | _isl.Map) -> bool: 853 | return other.is_strict_subset(self) 854 | 855 | 856 | def map_ge(self: _isl.BasicMap | _isl.Map, other: _isl.BasicMap | _isl.Map) -> bool: 857 | return other.is_subset(self) 858 | 859 | 860 | # {{{ project_out_except 861 | 862 | def obj_project_out_except( 863 | obj: SetOrMapT, 864 | names: Collection[str], 865 | types: Collection[_isl.dim_type] 866 | ) -> SetOrMapT: 867 | """ 868 | :param types: list of :class:`dim_type` determining 869 | the types of axes to project out 870 | :param names: names of axes matching the above which 871 | should be left alone by the projection 872 | 873 | .. versionadded:: 2011.3 874 | """ 875 | 876 | for tp in types: 877 | while True: 878 | space = obj.get_space() 879 | var_dict = space.get_var_dict(tp) 880 | 881 | all_indices = set(range(space.dim(tp))) 882 | leftover_indices = {var_dict[name][1] for name in names 883 | if name in var_dict} 884 | project_indices = all_indices-leftover_indices 885 | if not project_indices: 886 | break 887 | 888 | min_index = min(project_indices) 889 | count = 1 890 | while min_index+count in project_indices: 891 | count += 1 892 | 893 | obj = obj.project_out(tp, min_index, count) 894 | 895 | return obj 896 | 897 | # }}} 898 | 899 | 900 | # {{{ eliminate_except 901 | 902 | def obj_eliminate_except( 903 | obj: SetOrMapT, 904 | names: Collection[str], 905 | types: Collection[_isl.dim_type] 906 | ) -> SetOrMapT: 907 | """ 908 | :param types: list of :class:`dim_type` determining 909 | the types of axes to eliminate 910 | :param names: names of axes matching the above which 911 | should be left alone by the eliminate 912 | 913 | .. versionadded:: 2011.3 914 | """ 915 | 916 | for tp in types: 917 | space = obj.get_space() 918 | var_dict = space.get_var_dict(tp) 919 | to_eliminate = ( 920 | set(range(space.dim(tp))) 921 | - {var_dict[name][1] for name in names 922 | if name in var_dict}) 923 | 924 | while to_eliminate: 925 | min_index = min(to_eliminate) 926 | count = 1 927 | while min_index+count in to_eliminate: 928 | count += 1 929 | 930 | obj = obj.eliminate(tp, min_index, count) 931 | 932 | to_eliminate -= set(range(min_index, min_index+count)) 933 | 934 | return obj 935 | 936 | # }}} 937 | 938 | 939 | # {{{ add_constraints 940 | 941 | def obj_add_constraints(obj: BasicT, constraints: Iterable[_isl.Constraint]) -> BasicT: 942 | """ 943 | .. versionadded:: 2011.3 944 | """ 945 | 946 | for cns in constraints: 947 | obj = obj.add_constraint(cns) 948 | 949 | return obj 950 | 951 | # }}} 952 | 953 | 954 | def _add_functionality() -> None: 955 | _isl.dim_type.__reduce__ = dim_type_reduce 956 | 957 | # {{{ Context 958 | 959 | _isl.Context.__reduce__ = context_reduce 960 | _isl.Context.__eq__ = context_eq 961 | _isl.Context.__ne__ = context_ne 962 | 963 | # }}} 964 | 965 | # {{{ generic initialization, pickling 966 | 967 | for cls in ALL_CLASSES: 968 | if hasattr(cls, "read_from_str"): 969 | cls.__reduce__ = generic_reduce 970 | 971 | # }}} 972 | 973 | # {{{ printing 974 | 975 | for cls in ALL_CLASSES: 976 | if (hasattr(cls, "_base_name") 977 | and hasattr(_isl.Printer, f"print_{cls._base_name}")): 978 | cls.__str__ = generic_str 979 | cls.__repr__ = generic_repr 980 | 981 | if not hasattr(cls, "__hash__"): 982 | raise AssertionError(f"not hashable: {cls}") 983 | 984 | # }}} 985 | 986 | # {{{ Python set-like behavior 987 | 988 | _isl.BasicSet.__and__ = bset_and 989 | _isl.BasicSet.__rand__ = bset_and 990 | _isl.Set.__and__ = set_and 991 | _isl.Set.__rand__ = set_and 992 | for cls in [_isl.BasicSet, _isl.Set]: 993 | cls.__or__ = set_or 994 | cls.__ror__ = set_or 995 | cls.__sub__ = set_sub 996 | 997 | _isl.BasicMap.__and__ = bmap_and 998 | _isl.BasicMap.__rand__ = bmap_and 999 | _isl.Map.__and__ = map_and 1000 | _isl.Map.__rand__ = map_and 1001 | for cls in [_isl.BasicMap, _isl.Map]: 1002 | cls.__or__ = map_or 1003 | cls.__ror__ = map_or 1004 | cls.__sub__ = map_sub 1005 | 1006 | # }}} 1007 | 1008 | # {{{ Space 1009 | 1010 | _isl.Space.create_from_names = staticmethod(space_create_from_names) 1011 | _isl.Space.get_var_dict = space_get_var_dict 1012 | _isl.Space.get_id_dict = space_get_id_dict 1013 | 1014 | # }}} 1015 | 1016 | # {{{ coefficient wrangling 1017 | 1018 | for coeff_class in [_isl.Constraint, _isl.Aff]: 1019 | coeff_class.set_coefficients = obj_set_coefficients 1020 | coeff_class.set_coefficients_by_name = obj_set_coefficients_by_name 1021 | coeff_class.get_coefficients_by_name = obj_get_coefficients_by_name 1022 | 1023 | # }}} 1024 | 1025 | # {{{ Constraint 1026 | 1027 | _isl.Constraint.eq_from_names = staticmethod(eq_from_names) 1028 | _isl.Constraint.ineq_from_names = staticmethod(ineq_from_names) 1029 | 1030 | # }}} 1031 | 1032 | # {{{ BasicSet 1033 | 1034 | _isl.BasicSet.get_constraints = basic_obj_get_constraints 1035 | 1036 | # }}} 1037 | 1038 | # {{{ BasicMap 1039 | 1040 | _isl.BasicMap.get_constraints = basic_obj_get_constraints 1041 | 1042 | # }}} 1043 | 1044 | # {{{ Set 1045 | 1046 | _isl.Set.get_basic_sets = set_get_basic_sets 1047 | _isl.BasicSet.get_basic_sets = set_get_basic_sets 1048 | 1049 | # }}} 1050 | 1051 | # {{{ Map 1052 | 1053 | _isl.Map.get_basic_maps = map_get_basic_maps 1054 | 1055 | # }}} 1056 | 1057 | 1058 | # {{{ common functionality 1059 | 1060 | for cls in ALL_CLASSES: 1061 | if hasattr(cls, "get_space") and cls is not _isl.Space: 1062 | cls.get_id_dict = obj_get_id_dict 1063 | cls.get_var_dict = obj_get_var_dict 1064 | cls.get_var_ids = obj_get_var_ids 1065 | cls.get_var_names = obj_get_var_names 1066 | cls.get_var_names_not_none = obj_get_var_names_not_none 1067 | 1068 | # }}} 1069 | 1070 | # {{{ piecewise 1071 | 1072 | _isl.PwAff.get_pieces = pwaff_get_pieces 1073 | _isl.Aff.get_pieces = pwaff_get_pieces 1074 | _isl.PwAff.get_aggregate_domain = pw_get_aggregate_domain 1075 | 1076 | _isl.PwQPolynomial.get_pieces = pwqpolynomial_get_pieces 1077 | _isl.PwQPolynomial.get_aggregate_domain = pw_get_aggregate_domain 1078 | 1079 | # }}} 1080 | 1081 | _isl.QPolynomial.get_terms = qpolynomial_get_terms 1082 | 1083 | _isl.PwQPolynomial.eval_with_dict = pwqpolynomial_eval_with_dict 1084 | 1085 | # {{{ arithmetic 1086 | 1087 | for expr_like_class in ARITH_CLASSES: 1088 | expr_like_class.__add__ = expr_like_add 1089 | expr_like_class.__radd__ = expr_like_add 1090 | expr_like_class.__sub__ = expr_like_sub 1091 | expr_like_class.__rsub__ = expr_like_rsub 1092 | expr_like_class.__mul__ = expr_like_mul 1093 | expr_like_class.__rmul__ = expr_like_mul 1094 | expr_like_class.__neg__ = expr_like_class.neg 1095 | 1096 | for qpoly_class in [_isl.QPolynomial, _isl.PwQPolynomial]: 1097 | qpoly_class.__pow__ = qpoly_class.pow 1098 | 1099 | for aff_class in [_isl.Aff, _isl.PwAff]: 1100 | aff_class.__mod__ = aff_class.mod_val 1101 | aff_class.__floordiv__ = expr_like_floordiv 1102 | 1103 | # }}} 1104 | 1105 | # {{{ Val 1106 | 1107 | val_cls = _isl.Val 1108 | 1109 | val_cls.__add__ = val_cls.add 1110 | val_cls.__radd__ = val_cls.add 1111 | val_cls.__sub__ = val_cls.sub 1112 | val_cls.__rsub__ = val_rsub 1113 | val_cls.__mul__ = val_cls.mul 1114 | val_cls.__rmul__ = val_cls.mul 1115 | val_cls.__neg__ = val_cls.neg 1116 | val_cls.__mod__ = val_cls.mod 1117 | val_cls.__bool__ = val_cls.__nonzero__ = val_bool 1118 | 1119 | val_cls.__lt__ = val_cls.lt 1120 | val_cls.__gt__ = val_cls.gt 1121 | val_cls.__le__ = val_cls.le 1122 | val_cls.__ge__ = val_cls.ge 1123 | val_cls.__eq__ = val_cls.eq 1124 | val_cls.__ne__ = val_cls.ne 1125 | 1126 | val_cls.__repr__ = val_repr 1127 | val_cls.__str__ = val_cls.to_str 1128 | val_cls.to_python = val_to_python 1129 | 1130 | # }}} 1131 | 1132 | # {{{ rich comparisons 1133 | 1134 | for cls in [_isl.BasicSet, _isl.Set]: 1135 | cls.__lt__ = set_lt 1136 | cls.__le__ = set_le 1137 | cls.__gt__ = set_gt 1138 | cls.__ge__ = set_ge 1139 | 1140 | for cls in [_isl.BasicMap, _isl.Map]: 1141 | cls.__lt__ = map_lt 1142 | cls.__le__ = map_le 1143 | cls.__gt__ = map_gt 1144 | cls.__ge__ = map_ge 1145 | 1146 | # }}} 1147 | 1148 | for c in [_isl.BasicSet, _isl.BasicMap, _isl.Set, _isl.Map]: 1149 | c.project_out_except = obj_project_out_except 1150 | c.add_constraints = obj_add_constraints 1151 | 1152 | for c in [_isl.BasicSet, _isl.Set]: 1153 | c.eliminate_except = obj_eliminate_except 1154 | 1155 | 1156 | _add_functionality() 1157 | 1158 | 1159 | _DOWNCAST_RE = re.compile( 1160 | r"Downcast from :class:`([A-Za-z]+)` to :class:`([A-Za-z]+)`.") 1161 | 1162 | 1163 | _TO_METHODS = { 1164 | "PwAff": "to_pw_aff", 1165 | "PwMultiAff": "to_pw_multi_aff", 1166 | "UnionPwAff": "to_union_pw_aff", 1167 | "UnionPwMultiAff": "to_union_pw_multi_aff", 1168 | "LocalSpace": "to_local_space", 1169 | "Set": "to_set", 1170 | "UnionSet": "to_union_set", 1171 | "Map": "to_map", 1172 | "UnionMap": "to_union_map", 1173 | } 1174 | 1175 | 1176 | def _depr_downcast_wrapper( 1177 | f: Callable[Concatenate[object, P], ResultT], 1178 | ) -> Callable[Concatenate[object, P], ResultT]: 1179 | doc = f.__doc__ 1180 | assert doc is not None 1181 | m = _DOWNCAST_RE.search(doc) 1182 | assert m, doc 1183 | basic_cls_name = intern(m.group(1)) 1184 | tgt_cls_name = m.group(2) 1185 | 1186 | tgt_cls = cast("type", getattr(_isl, tgt_cls_name)) 1187 | is_overload = "Overloaded function" in doc 1188 | msg = (f"{basic_cls_name}.{f.__name__} " 1189 | f"with implicit conversion of self to {tgt_cls_name} is deprecated " 1190 | "and will stop working in 2026. " 1191 | f"Explicitly convert to {tgt_cls_name}, " 1192 | f"using .{_TO_METHODS[tgt_cls_name]}().") 1193 | 1194 | if is_overload: 1195 | def wrapper(self: object, *args: P.args, **kwargs: P.kwargs) -> ResultT: 1196 | # "Try to" detect bad invocations of, e.g., Set.union, which is 1197 | # an overload of normal union and UnionSet.union. 1198 | if ( 1199 | any(isinstance(arg, tgt_cls) for arg in args) 1200 | or 1201 | any(isinstance(arg, tgt_cls) for arg in kwargs.values()) 1202 | ): 1203 | warn(msg, DeprecationWarning, stacklevel=2) 1204 | 1205 | return f(self, *args, **kwargs) 1206 | else: 1207 | def wrapper(self: object, *args: P.args, **kwargs: P.kwargs) -> ResultT: 1208 | warn(msg, DeprecationWarning, stacklevel=2) 1209 | 1210 | return f(self, *args, **kwargs) 1211 | update_wrapper(wrapper, f) 1212 | return wrapper 1213 | 1214 | 1215 | def _monkeypatch_self_downcast_deprecation(): 1216 | for cls in ALL_CLASSES: 1217 | for attr_name in dir(cls): 1218 | val = cast("object", getattr(cls, attr_name)) 1219 | doc = getattr(val, "__doc__", None) 1220 | if doc and "\nDowncast from " in doc: 1221 | setattr(cls, attr_name, _depr_downcast_wrapper( 1222 | cast("Callable", val), # pyright: ignore[reportMissingTypeArgument] 1223 | )) 1224 | 1225 | 1226 | if not os.environ.get("ISLPY_NO_DOWNCAST_DEPRECATION", None): 1227 | _monkeypatch_self_downcast_deprecation() 1228 | --------------------------------------------------------------------------------