├── .basedpyright
└── baseline.json
├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yml
│ └── feature_request.md
├── dependabot.yml
└── workflows
│ ├── autopush.yml
│ ├── ci.yml
│ └── wheels.yml
├── .gitignore
├── .gitlab-ci.yml
├── .gitmodules
├── CITATION.cff
├── CMakeLists.txt
├── README.rst
├── build-with-barvinok.sh
├── doc
├── .gitignore
├── Makefile
├── conf.py
├── images
│ ├── after-union.png
│ └── before-union.png
├── index.rst
├── misc.rst
├── ref_ast.rst
├── ref_containers.rst
├── ref_expr.rst
├── ref_flow.rst
├── ref_fundamental.rst
├── ref_geo.rst
├── ref_schedule.rst
├── ref_set.rst
├── reference.rst
└── upload-docs.sh
├── examples
├── .gitignore
└── demo.py
├── gen_wrap.py
├── isl-supplementary
├── gitversion.h
├── isl
│ ├── config.h
│ └── stdint.h
└── isl_config.h
├── islpy
├── __init__.py
├── _monkeypatch.py
├── py.typed
└── version.py
├── pyproject.toml
├── src
└── wrapper
│ ├── wrap_helpers.hpp
│ ├── wrap_isl.cpp
│ ├── wrap_isl.hpp
│ ├── wrap_isl_part1.cpp
│ ├── wrap_isl_part2.cpp
│ └── wrap_isl_part3.cpp
├── stubgen
└── stubgen.py
└── test
└── test_isl.py
/.basedpyright/baseline.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | "./islpy/_isl.pyi": [
4 | {
5 | "code": "reportAssignmentType",
6 | "range": {
7 | "startColumn": 13,
8 | "endColumn": 15,
9 | "lineCount": 1
10 | }
11 | },
12 | {
13 | "code": "reportAssignmentType",
14 | "range": {
15 | "startColumn": 13,
16 | "endColumn": 15,
17 | "lineCount": 1
18 | }
19 | },
20 | {
21 | "code": "reportOverlappingOverload",
22 | "range": {
23 | "startColumn": 8,
24 | "endColumn": 12,
25 | "lineCount": 1
26 | }
27 | },
28 | {
29 | "code": "reportOverlappingOverload",
30 | "range": {
31 | "startColumn": 8,
32 | "endColumn": 12,
33 | "lineCount": 1
34 | }
35 | },
36 | {
37 | "code": "reportOverlappingOverload",
38 | "range": {
39 | "startColumn": 8,
40 | "endColumn": 12,
41 | "lineCount": 1
42 | }
43 | },
44 | {
45 | "code": "reportIncompatibleMethodOverride",
46 | "range": {
47 | "startColumn": 8,
48 | "endColumn": 14,
49 | "lineCount": 1
50 | }
51 | },
52 | {
53 | "code": "reportIncompatibleMethodOverride",
54 | "range": {
55 | "startColumn": 8,
56 | "endColumn": 14,
57 | "lineCount": 1
58 | }
59 | },
60 | {
61 | "code": "reportOverlappingOverload",
62 | "range": {
63 | "startColumn": 8,
64 | "endColumn": 12,
65 | "lineCount": 1
66 | }
67 | },
68 | {
69 | "code": "reportOverlappingOverload",
70 | "range": {
71 | "startColumn": 8,
72 | "endColumn": 12,
73 | "lineCount": 1
74 | }
75 | },
76 | {
77 | "code": "reportOverlappingOverload",
78 | "range": {
79 | "startColumn": 8,
80 | "endColumn": 19,
81 | "lineCount": 1
82 | }
83 | },
84 | {
85 | "code": "reportOverlappingOverload",
86 | "range": {
87 | "startColumn": 8,
88 | "endColumn": 12,
89 | "lineCount": 1
90 | }
91 | },
92 | {
93 | "code": "reportOverlappingOverload",
94 | "range": {
95 | "startColumn": 8,
96 | "endColumn": 12,
97 | "lineCount": 1
98 | }
99 | },
100 | {
101 | "code": "reportOverlappingOverload",
102 | "range": {
103 | "startColumn": 8,
104 | "endColumn": 38,
105 | "lineCount": 1
106 | }
107 | },
108 | {
109 | "code": "reportOverlappingOverload",
110 | "range": {
111 | "startColumn": 8,
112 | "endColumn": 37,
113 | "lineCount": 1
114 | }
115 | },
116 | {
117 | "code": "reportOverlappingOverload",
118 | "range": {
119 | "startColumn": 8,
120 | "endColumn": 37,
121 | "lineCount": 1
122 | }
123 | },
124 | {
125 | "code": "reportOverlappingOverload",
126 | "range": {
127 | "startColumn": 8,
128 | "endColumn": 36,
129 | "lineCount": 1
130 | }
131 | },
132 | {
133 | "code": "reportOverlappingOverload",
134 | "range": {
135 | "startColumn": 8,
136 | "endColumn": 23,
137 | "lineCount": 1
138 | }
139 | },
140 | {
141 | "code": "reportOverlappingOverload",
142 | "range": {
143 | "startColumn": 8,
144 | "endColumn": 22,
145 | "lineCount": 1
146 | }
147 | },
148 | {
149 | "code": "reportOverlappingOverload",
150 | "range": {
151 | "startColumn": 8,
152 | "endColumn": 12,
153 | "lineCount": 1
154 | }
155 | },
156 | {
157 | "code": "reportOverlappingOverload",
158 | "range": {
159 | "startColumn": 8,
160 | "endColumn": 38,
161 | "lineCount": 1
162 | }
163 | },
164 | {
165 | "code": "reportOverlappingOverload",
166 | "range": {
167 | "startColumn": 8,
168 | "endColumn": 37,
169 | "lineCount": 1
170 | }
171 | },
172 | {
173 | "code": "reportOverlappingOverload",
174 | "range": {
175 | "startColumn": 8,
176 | "endColumn": 37,
177 | "lineCount": 1
178 | }
179 | },
180 | {
181 | "code": "reportOverlappingOverload",
182 | "range": {
183 | "startColumn": 8,
184 | "endColumn": 36,
185 | "lineCount": 1
186 | }
187 | },
188 | {
189 | "code": "reportOverlappingOverload",
190 | "range": {
191 | "startColumn": 8,
192 | "endColumn": 23,
193 | "lineCount": 1
194 | }
195 | },
196 | {
197 | "code": "reportOverlappingOverload",
198 | "range": {
199 | "startColumn": 8,
200 | "endColumn": 22,
201 | "lineCount": 1
202 | }
203 | },
204 | {
205 | "code": "reportOverlappingOverload",
206 | "range": {
207 | "startColumn": 8,
208 | "endColumn": 12,
209 | "lineCount": 1
210 | }
211 | }
212 | ]
213 | }
214 | }
--------------------------------------------------------------------------------
/.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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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/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 |
9 | # vim: sw=4
10 |
--------------------------------------------------------------------------------
/.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@v4
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 |
--------------------------------------------------------------------------------
/.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@v4
20 | - uses: actions/setup-python@v5
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@v4
34 | -
35 | uses: actions/setup-python@v5
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@v4
48 | - uses: actions/setup-python@v5
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
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@v4
68 | -
69 | uses: actions/setup-python@v5
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@v4
85 | -
86 | uses: actions/setup-python@v5
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@v4
100 | -
101 | uses: actions/setup-python@v5
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@v4
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@v4
150 | -
151 | uses: actions/setup-python@v5
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 |
--------------------------------------------------------------------------------
/.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-13, macos-14, ubuntu-22.04-arm]
18 |
19 | steps:
20 | - uses: actions/checkout@v4
21 | with:
22 | submodules: recursive
23 | persist-credentials: false
24 |
25 | - name: Build wheels
26 | uses: pypa/cibuildwheel@v2.23.3
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@v4
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@v4
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@v4
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@v4
70 | with:
71 | pattern: cibw-*
72 | path: dist
73 | merge-multiple: true
74 |
75 | - uses: pypa/gh-action-pypi-publish@v1.12.4
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@v4
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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "isl"]
2 | path = isl
3 | url = https://github.com/inducer/isl.git
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
28 | if(USE_SHIPPED_ISL)
29 | if(USE_BARVINOK)
30 | message(FATAL_ERROR "Using barvinok is not compatible with shipped ISL")
31 | endif()
32 | set(ISL_SOURCES
33 | isl/isl_schedule.c
34 | isl/isl_ast_build_expr.c
35 | isl/isl_sample.c
36 | isl/isl_coalesce.c
37 | isl/isl_fold.c
38 | isl/isl_schedule_read.c
39 | isl/isl_aff_map.c
40 | isl/isl_scheduler_clustering.c
41 | isl/isl_flow.c
42 | isl/isl_map_subtract.c
43 | isl/uset_to_umap.c
44 | isl/isl_hash.c
45 | isl/isl_aff.c
46 | isl/isl_transitive_closure.c
47 | isl/isl_map_simplify.c
48 | isl/print.c
49 | isl/basis_reduction_tab.c
50 | isl/isl_schedule_constraints.c
51 | isl/isl_sort.c
52 | isl/isl_ast.c
53 | isl/bset_to_bmap.c
54 | isl/bset_from_bmap.c
55 | isl/isl_schedule_band.c
56 | isl/isl_bernstein.c
57 | isl/uset_from_umap.c
58 | isl/isl_scheduler.c
59 | isl/isl_set_to_ast_graft_list.c
60 | isl/isl_convex_hull.c
61 | isl/isl_schedule_tree.c
62 | isl/isl_tarjan.c
63 | isl/isl_equalities.c
64 | isl/isl_constraint.c
65 | isl/isl_union_map.c
66 | isl/isl_bound.c
67 | isl/isl_stride.c
68 | isl/set_list_from_map_list_inl.c
69 | isl/isl_farkas.c
70 | isl/isl_tab_pip.c
71 | isl/set_to_map.c
72 | isl/set_from_map.c
73 | isl/isl_lp.c
74 | isl/isl_ffs.c
75 | isl/isl_id_to_ast_expr.c
76 | isl/isl_val.c
77 | isl/isl_set_list.c
78 | isl/isl_space.c
79 | isl/isl_tab.c
80 | isl/isl_map.c
81 | isl/isl_version.c
82 | isl/isl_stream.c
83 | isl/isl_local_space.c
84 | isl/isl_id_to_pw_aff.c
85 | isl/isl_ilp.c
86 | isl/isl_range.c
87 | isl/isl_point.c
88 | isl/isl_schedule_node.c
89 | isl/isl_polynomial.c
90 | isl/isl_options.c
91 | isl/isl_morph.c
92 | isl/isl_deprecated.c
93 | isl/isl_ctx.c
94 | isl/isl_seq.c
95 | isl/isl_box.c
96 | isl/isl_output.c
97 | isl/isl_factorization.c
98 | isl/isl_printer.c
99 | isl/dep.c
100 | isl/isl_id_to_id.c
101 | isl/isl_ast_build.c
102 | isl/isl_ast_codegen.c
103 | isl/isl_obj.c
104 | isl/isl_scheduler_scc.c
105 | isl/isl_vec.c
106 | isl/isl_map_list.c
107 | isl/isl_vertices.c
108 | isl/isl_arg.c
109 | isl/isl_mat.c
110 | isl/isl_id.c
111 | isl/isl_affine_hull.c
112 | isl/isl_scan.c
113 | isl/isl_map_to_basic_set.c
114 | isl/isl_blk.c
115 | isl/isl_dim_map.c
116 | isl/isl_local.c
117 | isl/isl_reordering.c
118 | isl/isl_ast_graft.c
119 | isl/isl_input.c
120 | )
121 | set(ISL_INC_DIRS
122 | ${CMAKE_SOURCE_DIR}/isl-supplementary
123 | ${CMAKE_SOURCE_DIR}/isl/include
124 | ${CMAKE_SOURCE_DIR}/isl
125 | )
126 | if(USE_GMP_FOR_MP)
127 | list(APPEND ISL_SOURCES
128 | isl/isl_val_gmp.c
129 | )
130 | elseif(USE_IMATH_FOR_MP)
131 | if(USE_SHIPPED_IMATH)
132 | list(APPEND ISL_SOURCES
133 | isl/isl_imath.c
134 | isl/imath/imath.c
135 | isl/imath/imrat.c
136 | isl/imath/gmp_compat.c
137 | )
138 | list(APPEND ISL_INC_DIRS ${CMAKE_SOURCE_DIR}/isl/imath)
139 | endif()
140 | if(USE_IMATH_SIO)
141 | list(APPEND ISL_SOURCES
142 | isl/isl_int_sioimath.c
143 | isl/isl_val_sioimath.c
144 | )
145 | else()
146 | list(APPEND ISL_SOURCES
147 | isl/isl_val_imath.c
148 | )
149 | endif()
150 | endif()
151 | else()
152 | set(ISL_SOURCES)
153 | if(NOT ISL_LIB_NAMES)
154 | set(ISL_LIB_NAMES isl)
155 | if(USE_BARVINOK)
156 | list(PREPEND ISL_LIB_NAMES barvinok)
157 | endif()
158 | endif()
159 | endif()
160 |
161 | set(ISLPY_GENERATED_SOURCE
162 | ${CMAKE_BINARY_DIR}/generated/gen-expose-part1.inc
163 | ${CMAKE_BINARY_DIR}/generated/gen-expose-part2.inc
164 | ${CMAKE_BINARY_DIR}/generated/gen-expose-part3.inc
165 | ${CMAKE_BINARY_DIR}/generated/gen-wrap-part1.inc
166 | ${CMAKE_BINARY_DIR}/generated/gen-wrap-part2.inc
167 | ${CMAKE_BINARY_DIR}/generated/gen-wrap-part3.inc
168 | )
169 |
170 | if(USE_BARVINOK)
171 | set(ISLPY_GENERATION_FLAGS --barvinok)
172 | else()
173 | set(ISLPY_GENERATION_FLAGS)
174 | endif()
175 |
176 | add_custom_command(
177 | OUTPUT ${ISLPY_GENERATED_SOURCE}
178 | COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/gen_wrap.py
179 | -o ${CMAKE_BINARY_DIR}/generated
180 | -I ${ISL_INC_DIRS}
181 | ${ISLPY_GENERATION_FLAGS}
182 | )
183 |
184 | nanobind_add_module(
185 | _isl
186 | NB_STATIC # Build static libnanobind (the extension module itself remains a shared library)
187 | NOMINSIZE # Optimize for speed, not for size
188 | LTO # Enable LTO
189 | src/wrapper/wrap_isl.cpp
190 | src/wrapper/wrap_isl_part1.cpp
191 | src/wrapper/wrap_isl_part2.cpp
192 | src/wrapper/wrap_isl_part3.cpp
193 | ${ISL_SOURCES}
194 | ${ISLPY_GENERATED_SOURCE}
195 | )
196 | target_include_directories(_isl PRIVATE ${CMAKE_BINARY_DIR}/generated)
197 |
198 | # Work around https://github.com/inducer/islpy/issues/120.
199 | # See https://stackoverflow.com/questions/43554227/extern-inline-func-results-in-undefined-reference-error
200 | # for some context.
201 | set_source_files_properties(${ISL_SOURCES} PROPERTIES COMPILE_DEFINITIONS __OPTIMIZE_SIZE__)
202 |
203 | if(USE_IMATH_FOR_MP)
204 | target_compile_definitions(_isl PRIVATE USE_IMATH_FOR_MP=1)
205 | endif()
206 |
207 | if(USE_IMATH_SIO)
208 | target_compile_definitions(_isl PRIVATE USE_SMALL_INT_OPT=1)
209 | endif()
210 |
211 | if(USE_GMP_FOR_MP)
212 | target_compile_definitions(_isl PRIVATE USE_GMP_FOR_MP=1)
213 | endif()
214 |
215 | if(USE_BARVINOK)
216 | target_compile_definitions(_isl PRIVATE ISLPY_INCLUDE_BARVINOK=1)
217 | target_include_directories(_isl PRIVATE ${BARVINOK_INC_DIRS})
218 | target_link_directories(_isl PRIVATE ${BARVINOK_LIB_DIRS})
219 | target_link_libraries(_isl PRIVATE ${BARVINOK_LIB_NAMES})
220 | endif()
221 |
222 | target_include_directories(_isl PRIVATE ${ISL_INC_DIRS})
223 |
224 | if(USE_SHIPPED_ISL)
225 | target_compile_definitions(_isl PRIVATE GIT_HEAD_ID="included-with-islpy")
226 | else()
227 | target_link_directories(_isl PRIVATE ${ISL_LIB_DIRS})
228 | target_link_libraries(_isl PRIVATE ${ISL_LIB_NAMES})
229 | endif()
230 |
231 | install(TARGETS _isl LIBRARY DESTINATION islpy)
232 |
233 | set(ISLPY_STUB_FILE ${CMAKE_BINARY_DIR}/_isl.pyi)
234 | add_custom_command(
235 | OUTPUT ${ISLPY_STUB_FILE}
236 | COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/stubgen/stubgen.py
237 | -o ${CMAKE_BINARY_DIR}
238 | --exec ${CMAKE_SOURCE_DIR}/islpy/_monkeypatch.py
239 | --python-path ${CMAKE_BINARY_DIR}
240 | -m _isl
241 | DEPENDS _isl
242 | )
243 | add_custom_target(
244 | _isl_stub
245 | ALL DEPENDS ${CMAKE_BINARY_DIR}/_isl.pyi
246 | )
247 | install(FILES ${ISLPY_STUB_FILE} DESTINATION islpy)
248 |
249 | # vim: sw=2
250 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/build-with-barvinok.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | set -e
4 | set -x
5 |
6 | BUILD_DIR=$(mktemp -d -t islpy-barvinok-build-XXXXXXX)
7 | echo "BUILDING IN $BUILD_DIR"
8 |
9 | if test "$1" = ""; then
10 | echo "usage: $0 PREFIX_DIR [GMP_PREFIX_DIR]"
11 | fi
12 | PREFIX="$1"
13 | GMP_PREFIX="${2:-$PREFIX}"
14 | NTL_VER="10.5.0"
15 | BARVINOK_GIT_REV="barvinok-0.41.8"
16 | NPROCS=6
17 | ISLPY_SOURCE="$(pwd)"
18 |
19 | function with_echo()
20 | {
21 | echo "$@"
22 | "$@"
23 | }
24 |
25 | if true; then
26 | rm -Rf "$BUILD_DIR"
27 |
28 | mkdir "$BUILD_DIR"
29 | cd "$BUILD_DIR"
30 |
31 | rm -Rf islpy
32 | git clone "$ISLPY_SOURCE" --no-local
33 |
34 | curl -L -O --insecure http://shoup.net/ntl/ntl-"$NTL_VER".tar.gz
35 | tar xfz ntl-"$NTL_VER".tar.gz
36 | cd "$BUILD_DIR/ntl-$NTL_VER/src"
37 | ./configure NTL_GMP_LIP=on DEF_PREFIX="$PREFIX" GMP_PREFIX="$GMP_PREFIX" TUNE=x86 SHARED=on
38 | make -j$NPROCS
39 | make install
40 |
41 | cd "$BUILD_DIR"
42 | rm -Rf barvinok
43 | git clone https://github.com/inducer/barvinok.git
44 | cd barvinok
45 | git checkout $BARVINOK_GIT_REV
46 |
47 | numtries=1
48 | while ! ./get_submodules.sh; do
49 | sleep 5
50 | numtries=$((numtries+1))
51 | if test "$numtries" == 5; then
52 | echo "*** getting barvinok submodules failed even after a few tries"
53 | exit 1
54 | fi
55 | done
56 |
57 | sh autogen.sh
58 | ./configure \
59 | --prefix="$PREFIX" \
60 | --with-ntl-prefix="$PREFIX" \
61 | --with-gmp-prefix="$GMP_PREFIX" \
62 | --enable-shared-barvinok \
63 | --with-pet=no
64 |
65 | BARVINOK_ADDITIONAL_MAKE_ARGS=""
66 | if [ "$(uname)" == "Darwin" ]; then
67 | BARVINOK_ADDITIONAL_MAKE_ARGS=CFLAGS="-Wno-error=implicit-function-declaration"
68 | fi
69 | make $BARVINOK_ADDITIONAL_MAKE_ARGS -j$NPROCS
70 | make install
71 | fi
72 |
73 | cd "$BUILD_DIR"
74 | cd islpy
75 | export LD_LIBRARY_PATH="$PREFIX/lib:$LD_LIBRARY_PATH"
76 | python -m pip install . \
77 | --config-settings=cmake.define.USE_SHIPPED_ISL=OFF \
78 | --config-settings=cmake.define.USE_SHIPPED_IMATH=OFF \
79 | --config-settings=cmake.define.USE_BARVINOK=ON \
80 | --config-settings=cmake.define.ISL_INC_DIRS:LIST="$PREFIX/include " \
81 | --config-settings=cmake.define.ISL_LIB_DIRS:LIST="$PREFIX/lib"
82 |
83 | # vim: sw=2
84 |
--------------------------------------------------------------------------------
/doc/.gitignore:
--------------------------------------------------------------------------------
1 | _build
2 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/doc/images/after-union.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inducer/islpy/b9feb05b776701aacf6de52dc5a5c9be1606a0c6/doc/images/after-union.png
--------------------------------------------------------------------------------
/doc/images/before-union.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inducer/islpy/b9feb05b776701aacf6de52dc5a5c9be1606a0c6/doc/images/before-union.png
--------------------------------------------------------------------------------
/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/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::
20 |
21 | $ pip install --no-build-isolation -e .
22 |
23 | Support
24 | =======
25 |
26 | You can try posting questions or comments at the
27 | `Github Discussions site `__
28 | for islpy.
29 |
30 | For a mailing list, please consider using the `isl list
31 | `_ until they tell us to get
32 | lost.
33 |
34 | License
35 | =======
36 |
37 | islpy is licensed to you under the MIT/X Consortium license:
38 |
39 | Copyright (c) 2011 Andreas Klöckner and Contributors.
40 |
41 | Permission is hereby granted, free of charge, to any person
42 | obtaining a copy of this software and associated documentation
43 | files (the "Software"), to deal in the Software without
44 | restriction, including without limitation the rights to use,
45 | copy, modify, merge, publish, distribute, sublicense, and/or sell
46 | copies of the Software, and to permit persons to whom the
47 | Software is furnished to do so, subject to the following
48 | conditions:
49 |
50 | The above copyright notice and this permission notice shall be
51 | included in all copies or substantial portions of the Software.
52 |
53 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
54 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
55 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
56 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
57 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
58 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
59 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
60 | OTHER DEALINGS IN THE SOFTWARE.
61 |
62 | .. note::
63 |
64 | * isl and imath, which islpy depends on, are also licensed under the `MIT
65 | license `_.
66 |
67 | * GMP, which used to be a dependency of isl and thus islpy, is no longer
68 | required. (but building against it can optionally be requested)
69 |
70 | Relation with isl's C interface
71 | ===============================
72 |
73 | Nearly all of the bindings to isl are auto-generated, using the following
74 | rules:
75 |
76 | * Follow :pep:`8`.
77 | * Expose the underlying object-oriented structure.
78 | * Remove the `isl_` and `ISL_` prefixes from data types, macros and
79 | function names, replace them with Python namespaces.
80 | * A method `isl_printer_print_set` would thus become
81 | :meth:`islpy.Printer.print_set`.
82 |
83 | See also :ref:`gen-remarks`.
84 |
85 | User-visible Changes
86 | ====================
87 |
88 | Version 2016.2
89 | --------------
90 | .. note::
91 |
92 | This version is currently in development and can be obtained from
93 | islpy's version control.
94 |
95 | * Update for isl 0.17
96 | * Add :func:`islpy.make_zero_and_vars`
97 |
98 | Version 2016.1.1
99 | ----------------
100 |
101 | * Add :func:`islpy.make_zero_and_vars`
102 | * Do not turn on small-integer optimization by default
103 | (to avoid build trouble on old compilers)
104 |
105 | Version 2016.1
106 | --------------
107 |
108 | * Update for isl 0.16
109 |
110 | Version 2014.2.1
111 | ----------------
112 |
113 | * :mod:`islpy` now avoids using 2to3 for Python 3 compatibility.
114 |
115 | Version 2014.2
116 | --------------
117 |
118 | * A large number of previously unavailable functions are now exposed.
119 |
120 | * Sebastian Pop's `imath `__ support has
121 | been merged into the version of isl that ships with :mod:`islpy`. This means
122 | that unless a user specifically requests a build against GMP, :mod:`islpy`
123 | is (a) entirely self-contained and depends only on a C++ compiler and
124 | (b) is entirely MIT-licensed by default.
125 |
126 | Version 2014.1
127 | --------------
128 |
129 | * Many classes are now picklable.
130 |
131 | * isl's handling of integer's has changed, forcing islpy to make
132 | incompatible changes as well.
133 |
134 | Now :class:`islpy.Val` is used to represent all numbers going
135 | into and out of :mod:`islpy`. ``gmpy`` is no longer a dependency
136 | of :mod:`islpy`. The following rules apply for this interface change:
137 |
138 | * You can pass (up to ``long int``-sized) integers to methods of
139 | isl objects without manual conversion to :class:`islpy.Val`.
140 | For larger numbers, you need to convert manually for now.
141 |
142 | * All numbers returned from :mod:`islpy` will be of type :class:`islpy.Val`.
143 | If they are integers, they can be converted
144 |
145 | * Since upstream made the decision to make ``isl_XXX_do_something_val``
146 | not always semantically equivalent to ``isl_XXX_do_something``, the
147 | old functions were removed.
148 |
149 | One example of this is ``isl_aff_get_constant``, which returned just
150 | the constant, and ``isl_aff_get_constant_val``, which returns the
151 | constant divided by the :class:`islpy.Aff`'s denominator as a rational
152 | value.
153 |
154 | Version 2011.3
155 | --------------
156 |
157 | * Add :meth:`islpy.Set.project_out_except` and friends.
158 | * Add ``islpy.Set.remove_divs_of_dim_type`` and friends.
159 | * ``islpy.Dim`` was renamed to :class:`islpy.Space` in isl.
160 | * ``islpy.Div`` was removed and replaced by :class:`islpy.Aff`
161 | wherever it was used previously.
162 | * ``islpy.BasicSet.as_set`
163 | and
164 | ``islpy.BasicMap.as_map``
165 | were removed.
166 | * :ref:`automatic-casts` were added.
167 | * Support for more Python :class:`set`-like behavior was added. In particular,
168 | the operators `|`, `&', '-', `<`, `<=`, `>`, `>=`, `==`, `!=` work as expected.
169 | * Support direct construction from string for objects that have a `read_from_str`
170 | method.
171 | * The constant in a :class:`islpy.Constraint` is now set as the '1'
172 | key in a coefficient dictionary in
173 | :meth:`islpy.Constraint.eq_from_names`,
174 | :meth:`islpy.Constraint.ineq_from_names`, and
175 | :meth:`islpy.Constraint.set_coefficients_by_name`.
176 |
177 | Version 2011.2
178 | --------------
179 |
180 | * Switch to copy-by-default semantics.
181 | * A few changes in Python-side functionality.
182 | * Automatic type promotion in 'self' argument.
183 |
184 | Version 2011.1
185 | --------------
186 |
187 | * Initial release.
188 |
189 | Documentation Cross-References
190 | ------------------------------
191 |
192 | .. class:: unsigned
193 |
194 | See :class:`int`.
195 |
196 | .. class:: long
197 |
198 | See :class:`int`.
199 |
200 | .. class:: size_t
201 |
202 | See :class:`int`.
203 |
204 | .. class:: double
205 |
206 | See :class:`float`.
207 |
--------------------------------------------------------------------------------
/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_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
141 | .. autofunction:: align_spaces
142 | .. autofunction:: align_two
143 |
144 | Canonical Names for Internal Module
145 | -----------------------------------
146 |
147 | .. ::
148 |
149 | This should switch to using ``:canonical:`` once Sphinx 4.0 is released.
150 |
151 | .. currentmodule:: islpy._isl
152 |
153 | .. class:: stat
154 |
155 | A status result.
156 |
157 | .. class:: error
158 |
159 | See :class:`islpy.error`.
160 |
161 | .. class:: dim_type
162 |
163 | See :class:`islpy.dim_type`.
164 |
165 | .. class:: fold
166 |
167 | See :class:`islpy.fold`.
168 |
169 | .. class:: format
170 |
171 | See :class:`islpy.format`.
172 |
173 | .. class:: yaml_style
174 |
175 | See :class:`islpy.yaml_style`.
176 |
177 | .. class:: Printer
178 |
179 | See :class:`islpy.Printer`.
180 |
181 | .. currentmodule:: islpy._monkeypatch
182 |
183 | .. class:: SetLikeT
184 |
185 | A type variable with an upper bound of :class:`islpy.BasicSet` | :class:`islpy.Set`.
186 |
187 | .. class:: AffOrConstraintT
188 |
189 | A type variable with an upper bound of :class:`islpy.Aff` | :class:`islpy.Constraint`.
190 |
191 | .. class:: BasicT
192 |
193 | A type variable with an upper bound of :class:`islpy.BasicSet` | :class:`islpy.BasicMap`.
194 |
195 | .. vim: sw=4
196 |
--------------------------------------------------------------------------------
/doc/upload-docs.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | rsync --verbose --archive --delete _build/html/ doc-upload:doc/islpy
4 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | *.png
2 |
--------------------------------------------------------------------------------
/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 | bsets_in_union = []
17 | bset.union(bset2).convex_hull().foreach_basic_set(bsets_in_union.append)
18 | print(bsets_in_union)
19 | union, = bsets_in_union
20 | print("union: %s" % union)
21 | # ENDEXAMPLE
22 |
23 | import matplotlib.pyplot as pt
24 |
25 |
26 | def plot_basic_set(bset, *args, **kwargs):
27 | # This is a total hack. But it works for what it needs to do. :)
28 |
29 | plot_vert = kwargs.pop("plot_vert", False)
30 |
31 | vertices = []
32 | bset.compute_vertices().foreach_vertex(vertices.append)
33 |
34 | vertex_pts = []
35 |
36 | for v in vertices:
37 | points = []
38 | myset = isl.BasicSet.from_multi_aff(v.get_expr())
39 | myset.foreach_point(points.append)
40 | point, = points
41 | vertex_pts.append([
42 | point.get_coordinate_val(isl.dim_type.set, i).to_python()
43 | for i in range(2)])
44 |
45 | import numpy as np
46 | vertex_pts = np.array(vertex_pts)
47 |
48 | center = np.average(vertex_pts, axis=0)
49 |
50 | from math import atan2
51 | vertex_pts = np.array(
52 | sorted(vertex_pts, key=lambda x: atan2(x[1]-center[1], x[0]-center[0])))
53 |
54 | if plot_vert:
55 | pt.plot(vertex_pts[:, 0], vertex_pts[:, 1], "o")
56 |
57 | import matplotlib.patches as mpatches
58 | import matplotlib.path as mpath
59 |
60 | Path = mpath.Path # noqa
61 |
62 | codes = [Path.LINETO] * len(vertex_pts)
63 | codes[0] = Path.MOVETO
64 |
65 | pathdata = [
66 | (code, tuple(coord)) for code, coord in zip(codes, vertex_pts, strict=True)]
67 | pathdata.append((Path.CLOSEPOLY, (0, 0)))
68 |
69 | codes, verts = zip(*pathdata, strict=True)
70 | path = mpath.Path(verts, codes)
71 | patch = mpatches.PathPatch(path, **kwargs)
72 | pt.gca().add_patch(patch)
73 |
74 |
75 | plot_basic_set(bset, facecolor="red", edgecolor="black", alpha=0.3)
76 | plot_basic_set(bset2, facecolor="green", edgecolor="black", alpha=0.2)
77 | pt.grid()
78 | pt.xlim([-1, 6])
79 | pt.ylim([-1, 8])
80 | # pt.show()
81 | pt.savefig("before-union.png", dpi=50)
82 |
83 | plot_basic_set(union, facecolor="blue", edgecolor="yellow",
84 | alpha=0.5, plot_vert=True)
85 | pt.savefig("after-union.png", dpi=50)
86 |
--------------------------------------------------------------------------------
/isl-supplementary/gitversion.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inducer/islpy/b9feb05b776701aacf6de52dc5a5c9be1606a0c6/isl-supplementary/gitversion.h
--------------------------------------------------------------------------------
/isl-supplementary/isl/config.h:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/isl-supplementary/isl/stdint.h:
--------------------------------------------------------------------------------
1 | #include
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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
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 |
139 | # }}}
140 |
141 |
142 | DEFAULT_CONTEXT = Context()
143 |
144 |
145 | def _get_default_context() -> Context:
146 | """A callable to get the default context for the benefit of Python's
147 | ``__reduce__`` protocol.
148 | """
149 | return DEFAULT_CONTEXT
150 |
151 |
152 | def _back_to_basic(new_obj, old_obj):
153 | # Work around set_dim_id not being available for Basic{Set,Map}
154 | if isinstance(old_obj, BasicSet) and isinstance(new_obj, Set):
155 | bsets = new_obj.get_basic_sets()
156 |
157 | if len(bsets) == 0:
158 | bset = BasicSet.universe(new_obj.space).complement()
159 | else:
160 | bset, = bsets
161 |
162 | return bset
163 |
164 | if isinstance(old_obj, BasicMap) and isinstance(new_obj, Map):
165 | bmaps = new_obj.get_basic_maps()
166 |
167 | if len(bmaps) == 0:
168 | bmap = BasicMap.universe(new_obj.space).complement()
169 | else:
170 | bmap, = bmaps
171 |
172 | return bmap
173 |
174 | return new_obj
175 |
176 |
177 | def _set_dim_id(obj, dt, idx, id):
178 | return _back_to_basic(obj.set_dim_id(dt, idx, id), obj)
179 |
180 |
181 | def _align_dim_type(
182 | template_dt: dim_type,
183 | obj: AlignableT,
184 | template: AlignableT,
185 | obj_bigger_ok: bool,
186 | obj_names: Collection[str],
187 | template_names: Collection[str],
188 | ) -> AlignableT:
189 |
190 | # {{{ deal with Aff, PwAff
191 |
192 | # The technique below will not work for PwAff et al, because there is *only*
193 | # the 'param' dim_type, and we are not allowed to move dims around in there.
194 | # We'll make isl do the work, using align_params.
195 |
196 | if template_dt == dim_type.param and isinstance(obj, (Aff, PwAff)):
197 | if not isinstance(template, Space):
198 | template_space = template.space
199 | else:
200 | template_space = template
201 |
202 | if not obj_bigger_ok:
203 | if (obj.dim(template_dt) > template.dim(template_dt)
204 | or not set(obj.get_var_dict()) <= set(template.get_var_dict())):
205 | raise Error("obj has leftover dimensions after alignment")
206 | return obj.align_params(template_space)
207 |
208 | # }}}
209 |
210 | if None in template_names:
211 | all_nones = [None] * len(template_names)
212 | if template_names == all_nones and obj_names == all_nones:
213 | # that's ok
214 | return obj
215 |
216 | raise Error("template may not contain any unnamed dimensions")
217 |
218 | obj_names = set(obj_names) - {None}
219 | template_names = set(template_names) - {None}
220 |
221 | names_in_both = obj_names & template_names
222 |
223 | tgt_idx = 0
224 | while tgt_idx < template.dim(template_dt):
225 | tgt_id = template.get_dim_id(template_dt, tgt_idx)
226 | tgt_name = tgt_id.name
227 |
228 | if tgt_name in names_in_both:
229 | if (obj.dim(template_dt) > tgt_idx
230 | and tgt_name == obj.get_dim_name(template_dt, tgt_idx)):
231 | pass
232 |
233 | else:
234 | src_dt, src_idx = obj.get_var_dict()[tgt_name]
235 |
236 | if src_dt == template_dt:
237 | assert src_idx > tgt_idx
238 |
239 | # isl requires move_dims to be between different types.
240 | # Not sure why. Let's make it happy.
241 | other_dt = dim_type.param
242 | if src_dt == other_dt:
243 | other_dt = dim_type.out
244 |
245 | other_dt_dim = obj.dim(other_dt)
246 | obj = obj.move_dims(other_dt, other_dt_dim, src_dt, src_idx, 1)
247 | obj = obj.move_dims(
248 | template_dt, tgt_idx, other_dt, other_dt_dim, 1)
249 | else:
250 | obj = obj.move_dims(template_dt, tgt_idx, src_dt, src_idx, 1)
251 |
252 | # names are same, make Ids the same, too
253 | obj = _set_dim_id(obj, template_dt, tgt_idx, tgt_id)
254 |
255 | tgt_idx += 1
256 | else:
257 | obj = obj.insert_dims(template_dt, tgt_idx, 1)
258 | obj = _set_dim_id(obj, template_dt, tgt_idx, tgt_id)
259 |
260 | tgt_idx += 1
261 |
262 | if tgt_idx < obj.dim(template_dt) and not obj_bigger_ok:
263 | raise Error("obj has leftover dimensions after alignment")
264 |
265 | return obj
266 |
267 |
268 | def align_spaces(
269 | obj: AlignableT,
270 | template: Alignable,
271 | obj_bigger_ok: bool = False,
272 | ) -> AlignableT:
273 | """
274 | Try to make the space in which *obj* lives the same as that of *template* by
275 | adding/matching named dimensions.
276 |
277 | :param obj_bigger_ok: If *True*, no error is raised if the resulting *obj*
278 | has more dimensions than *template*.
279 | """
280 |
281 | have_any_param_domains = (
282 | isinstance(obj, (Set, BasicSet))
283 | and isinstance(template, (Set, BasicSet))
284 | and (obj.is_params() or template.is_params()))
285 | if have_any_param_domains:
286 | if obj.is_params():
287 | obj = type(obj).from_params(obj)
288 | if template.is_params():
289 | template = type(template).from_params(template)
290 |
291 | if isinstance(template, EXPR_CLASSES):
292 | dim_types = list(_CHECK_DIM_TYPES)
293 | dim_types.remove(dim_type.out)
294 | else:
295 | dim_types = _CHECK_DIM_TYPES
296 |
297 | obj_names = [
298 | obj.get_dim_name(dt, i)
299 | for dt in dim_types
300 | for i in range(obj.dim(dt))
301 | ]
302 | template_names = [
303 | template.get_dim_name(dt, i)
304 | for dt in dim_types
305 | for i in range(template.dim(dt))
306 | ]
307 |
308 | for dt in dim_types:
309 | obj = _align_dim_type(
310 | dt, obj, template, obj_bigger_ok, obj_names, template_names)
311 |
312 | return obj
313 |
314 |
315 | def align_two(
316 | obj1: AlignableT,
317 | obj2: AlignableT,
318 | ) -> tuple[AlignableT, AlignableT]:
319 | """Align the spaces of two objects, potentially modifying both of them.
320 |
321 | See also :func:`align_spaces`.
322 | """
323 |
324 | obj1 = align_spaces(obj1, obj2, obj_bigger_ok=True)
325 | obj2 = align_spaces(obj2, obj1, obj_bigger_ok=True)
326 | return (obj1, obj2)
327 |
328 |
329 | def make_zero_and_vars(
330 | set_vars: Sequence[str],
331 | params: Sequence[str] = (),
332 | ctx: Context | None = None
333 | ) -> dict[str | Literal[0], PwAff]:
334 | """
335 | :arg set_vars: an iterable of variable names, or a comma-separated string
336 | :arg params: an iterable of variable names, or a comma-separated string
337 |
338 | :return: a dictionary from variable names (in *set_vars* and *params*)
339 | to :class:`PwAff` instances that represent each of the
340 | variables. They key '0' is also include and represents
341 | a :class:`PwAff` zero constant.
342 |
343 | .. versionadded:: 2016.1.1
344 |
345 | This function is intended to make it relatively easy to construct sets
346 | programmatically without resorting to string manipulation.
347 |
348 | Usage example::
349 |
350 | v = isl.make_zero_and_vars("i,j,k", "n")
351 |
352 | myset = (
353 | v[0].le_set(v["i"] + v["j"])
354 | &
355 | (v["i"] + v["j"]).lt_set(v["n"])
356 | &
357 | (v[0].le_set(v["i"]))
358 | &
359 | (v["i"].le_set(13 + v["n"]))
360 | )
361 | """
362 | if ctx is None:
363 | ctx = DEFAULT_CONTEXT
364 |
365 | if isinstance(set_vars, str):
366 | set_vars = [s.strip() for s in set_vars.split(",")]
367 | if isinstance(params, str):
368 | params = [s.strip() for s in params.split(",")]
369 |
370 | space = Space.create_from_names(ctx, set=set_vars, params=params)
371 | return affs_from_space(space)
372 |
373 |
374 | def affs_from_space(space: Space) -> dict[Literal[0] | str, PwAff]:
375 | """
376 | :return: a dictionary from variable names (in *set_vars* and *params*)
377 | to :class:`PwAff` instances that represent each of the
378 | variables *in*space*. They key '0' is also include and represents
379 | a :class:`PwAff` zero constant.
380 |
381 | .. versionadded:: 2016.2
382 |
383 | This function is intended to make it relatively easy to construct sets
384 | programmatically without resorting to string manipulation.
385 |
386 | Usage example::
387 |
388 | s = isl.Set("[n] -> {[i,j,k]: 0<=i,j,k _isl.Context:
85 | ...
86 |
87 | def _wraps_same_instance_as(self, other: object) -> bool:
88 | ...
89 |
90 | _base_name: ClassVar[str]
91 |
92 | # }}}
93 |
94 |
95 | # {{{ copied verbatim from pytools to avoid numpy/pytools dependency
96 |
97 | F = TypeVar("F", bound=Callable[..., Any])
98 |
99 |
100 | class _HasKwargs:
101 | pass
102 |
103 |
104 | def _memoize_on_first_arg(function: F, cache_dict_name: str | None = None) -> F:
105 | """Like :func:`memoize_method`, but for functions that take the object
106 | in which do memoization information is stored as first argument.
107 |
108 | Supports cache deletion via ``function_name.clear_cache(self)``.
109 | """
110 | from sys import intern
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, *args, **kwargs):
118 | if kwargs:
119 | key = (_HasKwargs, frozenset(kwargs.items()), *args)
120 | else:
121 | key = args
122 |
123 | try:
124 | return getattr(obj, cache_dict_name)[key]
125 | except AttributeError:
126 | attribute_error = True
127 | except KeyError:
128 | attribute_error = False
129 |
130 | result = function(obj, *args, **kwargs)
131 | if attribute_error:
132 | object.__setattr__(obj, cache_dict_name, {key: result})
133 | return result
134 | else:
135 | getattr(obj, cache_dict_name)[key] = result
136 | return result
137 |
138 | def clear_cache(obj):
139 | object.__delattr__(obj, cache_dict_name)
140 |
141 | from functools import update_wrapper
142 | new_wrapper = update_wrapper(wrapper, function)
143 |
144 | # type-ignore because mypy has a point here, stuffing random attributes
145 | # into the function's dict is moderately sketchy.
146 | new_wrapper.clear_cache = clear_cache # type: ignore[attr-defined]
147 |
148 | return cast("F", new_wrapper)
149 |
150 | # }}}
151 |
152 |
153 | def _read_from_str_wrapper(cls, context, s, dims_with_apostrophes):
154 | """A callable to reconstitute instances from strings for the benefit
155 | of Python's ``__reduce__`` protocol.
156 | """
157 | cls_from_str = cls.read_from_str(context, s)
158 |
159 | # Apostrophes in dim names have been lost, put them back
160 | for dim_name, (dt, dim_idx) in dims_with_apostrophes.items():
161 | cls_from_str = cls_from_str.set_dim_name(dt, dim_idx, dim_name)
162 |
163 | return cls_from_str
164 |
165 |
166 | def dim_type_reduce(self: _isl.dim_type):
167 | return (_isl.dim_type, (int(self),))
168 |
169 |
170 | def context_reduce(self: _isl.Context):
171 | from islpy import DEFAULT_CONTEXT, _get_default_context
172 | if self._wraps_same_instance_as(DEFAULT_CONTEXT):
173 | return (_get_default_context, ())
174 | else:
175 | return (_isl.Context, ())
176 |
177 |
178 | def context_eq(self: IslObject, other: object):
179 | return isinstance(other, _isl.Context) and self._wraps_same_instance_as(other)
180 |
181 |
182 | def context_ne(self: object, other: object) -> bool:
183 | return not self.__eq__(other)
184 |
185 |
186 | def generic_reduce(self: IslObject):
187 | ctx = self.get_ctx()
188 | prn = _isl.Printer.to_str(ctx)
189 | prn = getattr(prn, f"print_{self._base_name}")(self)
190 |
191 | # Reconstructing from string will remove apostrophes in dim names,
192 | # so keep track of dim names with apostrophes
193 | dims_with_apostrophes = {
194 | dname: pos for dname, pos in self.get_var_dict().items()
195 | if "'" in dname}
196 |
197 | return (
198 | _read_from_str_wrapper,
199 | (type(self), ctx, prn.get_str(), dims_with_apostrophes))
200 |
201 |
202 | def generic_str(self: IslObject) -> str:
203 | prn = _isl.Printer.to_str(self.get_ctx())
204 | getattr(prn, f"print_{self._base_name}")(self)
205 | return prn.get_str()
206 |
207 |
208 | def generic_repr(self: IslObject) -> str:
209 | prn = _isl.Printer.to_str(self.get_ctx())
210 | getattr(prn, f"print_{self._base_name}")(self)
211 | return f'{type(self).__name__}("{prn.get_str()}")'
212 |
213 |
214 | def space_get_id_dict(
215 | self: _isl.Space,
216 | dimtype: _isl.dim_type | None = None
217 | ) -> Mapping[_isl.Id, tuple[_isl.dim_type, int]]:
218 | """Return a dictionary mapping variable :class:`Id` instances to tuples
219 | of (:class:`dim_type`, index).
220 |
221 | :param dimtype: None to get all variables, otherwise
222 | one of :class:`dim_type`.
223 | """
224 | result = {}
225 |
226 | def set_dim_id(name, tp, idx):
227 | if name in result:
228 | raise RuntimeError(f"non-unique var id '{name}' encountered")
229 | result[name] = tp, idx
230 |
231 | if dimtype is None:
232 | types = _CHECK_DIM_TYPES
233 | else:
234 | types = [dimtype]
235 |
236 | for tp in types:
237 | for i in range(self.dim(tp)):
238 | name = self.get_dim_id(tp, i)
239 | if name is not None:
240 | set_dim_id(name, tp, i)
241 |
242 | return result
243 |
244 |
245 | def space_get_var_dict(
246 | self: _isl.Space,
247 | dimtype: _isl.dim_type | None = None,
248 | ignore_out: bool = False
249 | ) -> Mapping[str, tuple[_isl.dim_type, int]]:
250 | """Return a dictionary mapping variable names to tuples of
251 | (:class:`dim_type`, index).
252 |
253 | :param dimtype: None to get all variables, otherwise
254 | one of :class:`dim_type`.
255 | """
256 | result: dict[str, tuple[_isl.dim_type, int]] = {}
257 |
258 | def set_dim_name(name: str, tp: _isl.dim_type, idx: int):
259 | if name in result:
260 | raise RuntimeError(f"non-unique var name '{name}' encountered")
261 | result[name] = tp, idx
262 |
263 | if dimtype is None:
264 | types = list(_CHECK_DIM_TYPES)
265 | if ignore_out:
266 | types = types[:]
267 | types.remove(_isl.dim_type.out)
268 | else:
269 | types = [dimtype]
270 |
271 | for tp in types:
272 | for i in range(self.dim(tp)):
273 | name = self.get_dim_name(tp, i)
274 | if name is not None:
275 | set_dim_name(name, tp, i)
276 |
277 | return result
278 |
279 |
280 | def space_create_from_names(
281 | ctx: _isl.Context,
282 | set: Sequence[str] | None = None,
283 | in_: Sequence[str] | None = None,
284 | out: Sequence[str] | None = None,
285 | params: Sequence[str] = ()
286 | ) -> _isl.Space:
287 | """Create a :class:`Space` from lists of variable names.
288 |
289 | :param set_: names of `set`-type variables.
290 | :param in_: names of `in`-type variables.
291 | :param out: names of `out`-type variables.
292 | :param params: names of parameter-type variables.
293 | """
294 | dt = _isl.dim_type
295 |
296 | if set is not None:
297 | if in_ is not None or out is not None:
298 | raise RuntimeError("must pass only one of set / (in_,out)")
299 |
300 | result = _isl.Space.set_alloc(ctx, nparam=len(params),
301 | dim=len(set))
302 |
303 | for i, name in enumerate(set):
304 | result = result.set_dim_name(dt.set, i, name)
305 |
306 | elif in_ is not None and out is not None:
307 | if set is not None:
308 | raise RuntimeError("must pass only one of set / (in_,out)")
309 |
310 | result = _isl.Space.alloc(ctx, nparam=len(params),
311 | n_in=len(in_), n_out=len(out))
312 |
313 | for i, name in enumerate(in_):
314 | result = result.set_dim_name(dt.in_, i, name)
315 |
316 | for i, name in enumerate(out):
317 | result = result.set_dim_name(dt.out, i, name)
318 | else:
319 | raise RuntimeError("invalid parameter combination")
320 |
321 | for i, name in enumerate(params):
322 | result = result.set_dim_name(dt.param, i, name)
323 |
324 | return result
325 |
326 |
327 | def obj_or(self: SetOrMapT, other: object) -> SetOrMapT:
328 | try:
329 | return self.union(other)
330 | except TypeError:
331 | return NotImplemented
332 |
333 |
334 | def obj_and(self: SetOrMapT, other: object) -> SetOrMapT:
335 | try:
336 | return self.intersect(other)
337 | except TypeError:
338 | return NotImplemented
339 |
340 |
341 | def obj_sub(self: SetOrMapT, other: object) -> SetOrMapT:
342 | try:
343 | return self.subtract(other)
344 | except TypeError:
345 | return NotImplemented
346 |
347 |
348 | def obj_set_coefficients(
349 | self: AffOrConstraintT,
350 | dim_tp: _isl.dim_type,
351 | args: Sequence[_isl.Val | int],
352 | ) -> AffOrConstraintT:
353 | """
354 | :param dim_tp: :class:`dim_type`
355 | :param args: :class:`list` of coefficients, for indices `0..len(args)-1`.
356 |
357 | .. versionchanged:: 2011.3
358 | New for :class:`Aff`
359 | """
360 | for i, coeff in enumerate(args):
361 | self = self.set_coefficient_val(dim_tp, i, coeff)
362 |
363 | return self
364 |
365 |
366 | def obj_set_coefficients_by_name(
367 | self: AffOrConstraintT,
368 | iterable: Iterable[tuple[str | Literal[1], _isl.Val | int]]
369 | | Mapping[str | Literal[1], _isl.Val | int],
370 | name_to_dim: Mapping[str, tuple[_isl.dim_type, int]] | None = None,
371 | ) -> AffOrConstraintT:
372 | """Set the coefficients and the constant.
373 |
374 | :param iterable: a :class:`dict` or iterable of :class:`tuple`
375 | instances mapping variable names to their coefficients.
376 | The constant is set to the value of the key '1'.
377 |
378 | .. versionchanged:: 2011.3
379 | New for :class:`Aff`
380 | """
381 | try:
382 | coeff_iterable: Iterable[tuple[str | Literal[1], _isl.Val | int]] = \
383 | list(iterable.items())
384 | except AttributeError:
385 | coeff_iterable = \
386 | cast("Iterable[tuple[str | Literal[1], _isl.Val | int]]", iterable)
387 |
388 | if name_to_dim is None:
389 | name_to_dim = obj_get_var_dict(self)
390 |
391 | for name, coeff in coeff_iterable:
392 | if name == 1:
393 | self = self.set_constant_val(coeff)
394 | else:
395 | assert name
396 | tp, idx = name_to_dim[name]
397 | self = self.set_coefficient_val(tp, idx, coeff)
398 |
399 | return self
400 |
401 |
402 | def obj_get_coefficients_by_name(
403 | self: _isl.Constraint | _isl.Aff,
404 | dimtype: _isl.dim_type | None = None,
405 | dim_to_name: Mapping[tuple[_isl.dim_type, int], str] | None = None,
406 | ) -> dict[str | Literal[1], _isl.Val]:
407 | """Return a dictionary mapping variable names to coefficients.
408 |
409 | :param dimtype: None to get all variables, otherwise
410 | one of :class:`dim_type`.
411 |
412 | .. versionchanged:: 2011.3
413 | New for :class:`Aff`
414 | """
415 | if dimtype is None:
416 | types: Sequence[_isl.dim_type] = _CHECK_DIM_TYPES
417 | else:
418 | types = [dimtype]
419 |
420 | result: dict[Literal[1] | str, _isl.Val] = {}
421 | for tp in types:
422 | for i in range(self.get_space().dim(tp)):
423 | coeff = self.get_coefficient_val(tp, i)
424 | if coeff:
425 | if dim_to_name is None:
426 | name = self.get_dim_name(tp, i)
427 | assert name
428 | else:
429 | name = dim_to_name[tp, i]
430 |
431 | result[name] = coeff
432 |
433 | const = self.get_constant_val()
434 | if const:
435 | result[1] = const
436 |
437 | return result
438 |
439 |
440 | def eq_from_names(
441 | space: _isl.Space,
442 | coefficients: Mapping[str | Literal[1], _isl.Val | int] | None = None
443 | ) -> _isl.Constraint:
444 | """Create a constraint `const + coeff_1*var_1 +... == 0`.
445 |
446 | :param space: :class:`Space`
447 | :param coefficients: a :class:`dict` or iterable of :class:`tuple`
448 | instances mapping variable names to their coefficients
449 | The constant is set to the value of the key '1'.
450 |
451 | .. versionchanged:: 2011.3
452 | Eliminated the separate *const* parameter.
453 | """
454 | if coefficients is None:
455 | coefficients = {}
456 | c = _isl.Constraint.equality_alloc(space)
457 | return obj_set_coefficients_by_name(c, coefficients)
458 |
459 |
460 | def ineq_from_names(
461 | space: _isl.Space,
462 | coefficients: Mapping[str | Literal[1], _isl.Val | int] | None = None
463 | ) -> _isl.Constraint:
464 | """Create a constraint `const + coeff_1*var_1 +... >= 0`.
465 |
466 | :param space: :class:`Space`
467 | :param coefficients: a :class:`dict` or iterable of :class:`tuple`
468 | instances mapping variable names to their coefficients
469 | The constant is set to the value of the key '1'.
470 |
471 | .. versionchanged:: 2011.3
472 | Eliminated the separate *const* parameter.
473 | """
474 | if coefficients is None:
475 | coefficients = {}
476 | c = _isl.Constraint.inequality_alloc(space)
477 | return obj_set_coefficients_by_name(c, coefficients)
478 |
479 |
480 | def basic_obj_get_constraints(
481 | self: _isl.BasicSet | _isl.BasicMap
482 | ) -> list[_isl.Constraint]:
483 | """Get a list of constraints."""
484 | result: list[_isl.Constraint] = []
485 | self.foreach_constraint(result.append)
486 | return result
487 |
488 |
489 | def set_get_basic_sets(self: _isl.Set | _isl.BasicSet) -> list[_isl.BasicSet]:
490 | """Get the list of :class:`BasicSet` instances in this :class:`Set`."""
491 | result: list[_isl.BasicSet] = []
492 | self.foreach_basic_set(result.append)
493 | return result
494 |
495 |
496 | def map_get_basic_maps(self: _isl.Map) -> list[_isl.BasicMap]:
497 | """Get the list of :class:`BasicMap` instances in this :class:`Map`."""
498 | result: list[_isl.BasicMap] = []
499 | self.foreach_basic_map(result.append)
500 | return result
501 |
502 |
503 | def obj_get_id_dict(
504 | self: HasSpace,
505 | dimtype: _isl.dim_type | None = None
506 | ) -> Mapping[_isl.Id, tuple[_isl.dim_type, int]]:
507 | """Return a dictionary mapping :class:`Id` instances to tuples of
508 | (:class:`dim_type`, index).
509 |
510 | :param dimtype: None to get all variables, otherwise
511 | one of :class:`dim_type`.
512 | """
513 | return self.get_space().get_id_dict(dimtype)
514 |
515 |
516 | @_memoize_on_first_arg
517 | def obj_get_var_dict(
518 | self: HasSpace,
519 | dimtype: _isl.dim_type | None = None
520 | ) -> Mapping[str, tuple[_isl.dim_type, int]]:
521 | """Return a dictionary mapping variable names to tuples of
522 | (:class:`dim_type`, index).
523 |
524 | :param dimtype: None to get all variables, otherwise
525 | one of :class:`dim_type`.
526 | """
527 | return self.get_space().get_var_dict(
528 | dimtype, ignore_out=isinstance(self, EXPR_CLASSES))
529 |
530 |
531 | def obj_get_var_ids(
532 | self: HasSpace,
533 | dimtype: _isl.dim_type
534 | ) -> Sequence[str]:
535 | """Return a list of :class:`Id` instances for :class:`dim_type` *dimtype*."""
536 | return [self.get_dim_name(dimtype, i) for i in range(self.dim(dimtype))]
537 |
538 |
539 | @_memoize_on_first_arg
540 | def obj_get_var_names(self: HasSpace, dimtype: _isl.dim_type) -> Sequence[str]:
541 | """Return a list of dim names (in order) for :class:`dim_type` *dimtype*."""
542 | return [self.get_dim_name(dimtype, i)
543 | for i in range(self.dim(dimtype))]
544 |
545 |
546 | def pwaff_get_pieces(self: _isl.PwAff | _isl.Aff) -> list[tuple[_isl.Set, _isl.Aff]]:
547 | result: list[tuple[_isl.Set, _isl.Aff]] = []
548 |
549 | def append_tuple(s: _isl.Set, v: _isl.Aff):
550 | result.append((s, v))
551 |
552 | self.foreach_piece(append_tuple)
553 | return result
554 |
555 |
556 | def pwqpolynomial_get_pieces(
557 | self: _isl.PwQPolynomial
558 | ) -> list[tuple[_isl.Set, _isl.QPolynomial]]:
559 | """
560 | :return: list of (:class:`Set`, :class:`QPolynomial`)
561 | """
562 |
563 | result: list[tuple[_isl.Set, _isl.QPolynomial]] = []
564 |
565 | def append_tuple(s: _isl.Set, v: _isl.QPolynomial):
566 | result.append((s, v))
567 |
568 | self.foreach_piece(append_tuple)
569 | return result
570 |
571 |
572 | def pw_get_aggregate_domain(self: _isl.PwAff | _isl.PwQPolynomial) -> _isl.Set:
573 | """
574 | :return: a :class:`Set` that is the union of the domains of all pieces
575 | """
576 |
577 | result = _isl.Set.empty(self.get_domain_space())
578 | for dom, _ in self.get_pieces():
579 | result = result.union(cast("_isl.Set", dom))
580 |
581 | return result
582 |
583 |
584 | def qpolynomial_get_terms(self: _isl.QPolynomial) -> list[_isl.Term]:
585 | """Get the list of :class:`Term` instances in this :class:`QPolynomial`."""
586 | result: list[_isl.Term] = []
587 | self.foreach_term(result.append)
588 | return result
589 |
590 |
591 | def pwqpolynomial_eval_with_dict(
592 | self: _isl.PwQPolynomial,
593 | value_dict: Mapping[str, int | _isl.Val]
594 | ) -> int:
595 | """Evaluates *self* for the parameters specified by
596 | *value_dict*, which maps parameter names to their values.
597 | """
598 |
599 | pt = _isl.Point.zero(self.space.params())
600 |
601 | for i in range(self.space.dim(_isl.dim_type.param)):
602 | par_name = self.space.get_dim_name(_isl.dim_type.param, i)
603 | assert par_name
604 | pt = pt.set_coordinate_val(
605 | _isl.dim_type.param, i, value_dict[par_name])
606 |
607 | return self.eval(pt).to_python()
608 |
609 |
610 | def _number_to_expr_like(template: ExprLikeT, num: int | _isl.Val) -> ExprLikeT:
611 | number_aff = _isl.Aff.zero_on_domain(template.get_domain_space())
612 | number_aff = number_aff.set_constant_val(num)
613 |
614 | if isinstance(template, _isl.Aff):
615 | return number_aff
616 | if isinstance(template, _isl.QPolynomial):
617 | return _isl.QPolynomial.from_aff(number_aff)
618 |
619 | # everything else is piecewise
620 |
621 | if template.get_pieces():
622 | number_pw_aff = _isl.PwAff.empty(template.get_space())
623 | for set, _ in template.get_pieces():
624 | number_pw_aff = set.indicator_function().cond(
625 | number_aff, number_pw_aff)
626 | else:
627 | number_pw_aff = _isl.PwAff.alloc(
628 | _isl.Set.universe(template.domain().space),
629 | number_aff)
630 |
631 | if isinstance(template, _isl.PwAff):
632 | return number_pw_aff
633 |
634 | elif isinstance(template, _isl.PwQPolynomial):
635 | return _isl.PwQPolynomial.from_pw_aff(number_pw_aff)
636 |
637 | else:
638 | raise TypeError("unexpected template type")
639 |
640 |
641 | def expr_like_add(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT:
642 | if not isinstance(other, ExprLike):
643 | other = _number_to_expr_like(self, other)
644 |
645 | try:
646 | return self.add(other)
647 | except TypeError:
648 | return NotImplemented
649 |
650 |
651 | def expr_like_sub(self: ExprLikeT, other: ExprLikeT | int | _isl.Val):
652 | if not isinstance(other, ExprLike):
653 | other = _number_to_expr_like(self, other)
654 |
655 | try:
656 | return self.sub(other)
657 | except TypeError:
658 | return NotImplemented
659 |
660 |
661 | def expr_like_rsub(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT:
662 | if not isinstance(other, ExprLike):
663 | other = _number_to_expr_like(self, other)
664 |
665 | return -self + other
666 |
667 |
668 | def expr_like_mul(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT:
669 | if not isinstance(other, ExprLike):
670 | other = _number_to_expr_like(self, other)
671 |
672 | try:
673 | return self.mul(other)
674 | except TypeError:
675 | return NotImplemented
676 |
677 |
678 | def expr_like_floordiv(self: AffLikeT, other: _isl.Val) -> AffLikeT:
679 | return self.scale_down_val(other).floor()
680 |
681 |
682 | def val_rsub(self: _isl.Val, other: _isl.Val) -> _isl.Val:
683 | return -self + other
684 |
685 |
686 | def val_bool(self: _isl.Val) -> bool:
687 | return not self.is_zero()
688 |
689 |
690 | def val_repr(self: _isl.Val) -> str:
691 | return f'{type(self).__name__}("{self.to_str()}")'
692 |
693 |
694 | def val_to_python(self: _isl.Val) -> int:
695 | if not self.is_int():
696 | raise ValueError("can only convert integer Val to python")
697 |
698 | return int(self.to_str())
699 |
700 |
701 | def obj_eq(self: IslObject, other: object) -> bool:
702 | assert self.get_ctx() == other.get_ctx(), (
703 | "Equality-comparing two objects from different ISL Contexts "
704 | "will likely lead to entertaining (but never useful) results. "
705 | "In particular, Spaces with matching names will no longer be "
706 | "equal.")
707 |
708 | return self.is_equal(other)
709 |
710 |
711 | def obj_ne(self: object, other: object) -> bool:
712 | return not self.__eq__(other)
713 |
714 |
715 | for cls in ALL_CLASSES:
716 | if hasattr(cls, "is_equal"):
717 | cls.__eq__ = obj_eq
718 | cls.__ne__ = obj_ne
719 |
720 |
721 | def obj_lt(self: SetOrMapT, other: SetOrMapT) -> bool:
722 | return self.is_strict_subset(other)
723 |
724 |
725 | def obj_le(self: SetOrMapT, other: SetOrMapT) -> bool:
726 | return self.is_subset(other)
727 |
728 |
729 | def obj_gt(self: SetOrMapT, other: SetOrMapT) -> bool:
730 | return other.is_strict_subset(self)
731 |
732 |
733 | def obj_ge(self: SetOrMapT, other: SetOrMapT) -> bool:
734 | return other.is_subset(self)
735 |
736 |
737 | # {{{ project_out_except
738 |
739 | def obj_project_out_except(
740 | obj: SetLikeT,
741 | names: Collection[str],
742 | types: Collection[_isl.dim_type]
743 | ) -> SetLikeT:
744 | """
745 | :param types: list of :class:`dim_type` determining
746 | the types of axes to project out
747 | :param names: names of axes matching the above which
748 | should be left alone by the projection
749 |
750 | .. versionadded:: 2011.3
751 | """
752 |
753 | for tp in types:
754 | while True:
755 | space = obj.get_space()
756 | var_dict = space.get_var_dict(tp)
757 |
758 | all_indices = set(range(space.dim(tp)))
759 | leftover_indices = {var_dict[name][1] for name in names
760 | if name in var_dict}
761 | project_indices = all_indices-leftover_indices
762 | if not project_indices:
763 | break
764 |
765 | min_index = min(project_indices)
766 | count = 1
767 | while min_index+count in project_indices:
768 | count += 1
769 |
770 | obj = obj.project_out(tp, min_index, count)
771 |
772 | return obj
773 |
774 | # }}}
775 |
776 |
777 | # {{{ eliminate_except
778 |
779 | def obj_eliminate_except(
780 | obj: SetLikeT,
781 | names: Collection[str],
782 | types: Collection[_isl.dim_type]
783 | ) -> SetLikeT:
784 | """
785 | :param types: list of :class:`dim_type` determining
786 | the types of axes to eliminate
787 | :param names: names of axes matching the above which
788 | should be left alone by the eliminate
789 |
790 | .. versionadded:: 2011.3
791 | """
792 |
793 | for tp in types:
794 | space = obj.get_space()
795 | var_dict = space.get_var_dict(tp)
796 | to_eliminate = (
797 | set(range(space.dim(tp)))
798 | - {var_dict[name][1] for name in names
799 | if name in var_dict})
800 |
801 | while to_eliminate:
802 | min_index = min(to_eliminate)
803 | count = 1
804 | while min_index+count in to_eliminate:
805 | count += 1
806 |
807 | obj = obj.eliminate(tp, min_index, count)
808 |
809 | to_eliminate -= set(range(min_index, min_index+count))
810 |
811 | return obj
812 |
813 | # }}}
814 |
815 |
816 | # {{{ add_constraints
817 |
818 | def obj_add_constraints(obj: BasicT, constraints: Iterable[_isl.Constraint]) -> BasicT:
819 | """
820 | .. versionadded:: 2011.3
821 | """
822 |
823 | for cns in constraints:
824 | obj = obj.add_constraint(cns)
825 |
826 | return obj
827 |
828 | # }}}
829 |
830 |
831 | def _add_functionality() -> None:
832 | _isl.dim_type.__reduce__ = dim_type_reduce
833 |
834 | # {{{ Context
835 |
836 | _isl.Context.__reduce__ = context_reduce
837 | _isl.Context.__eq__ = context_eq
838 | _isl.Context.__ne__ = context_ne
839 |
840 | # }}}
841 |
842 | # {{{ generic initialization, pickling
843 |
844 | for cls in ALL_CLASSES:
845 | if hasattr(cls, "read_from_str"):
846 | cls.__reduce__ = generic_reduce
847 |
848 | # }}}
849 |
850 | # {{{ printing
851 |
852 | for cls in ALL_CLASSES:
853 | if (hasattr(cls, "_base_name")
854 | and hasattr(_isl.Printer, f"print_{cls._base_name}")):
855 | cls.__str__ = generic_str
856 | cls.__repr__ = generic_repr
857 |
858 | if not hasattr(cls, "__hash__"):
859 | raise AssertionError(f"not hashable: {cls}")
860 |
861 | # }}}
862 |
863 | # {{{ Python set-like behavior
864 |
865 | for cls in [_isl.BasicSet, _isl.BasicMap, _isl.Set, _isl.Map]:
866 | cls.__or__ = obj_or
867 | cls.__ror__ = obj_or
868 | cls.__and__ = obj_and
869 | cls.__rand__ = obj_and
870 | cls.__sub__ = obj_sub
871 |
872 | # }}}
873 |
874 | # {{{ Space
875 |
876 | _isl.Space.create_from_names = staticmethod(space_create_from_names)
877 | _isl.Space.get_var_dict = space_get_var_dict
878 | _isl.Space.get_id_dict = space_get_id_dict
879 |
880 | # }}}
881 |
882 | # {{{ coefficient wrangling
883 |
884 | for coeff_class in [_isl.Constraint, _isl.Aff]:
885 | coeff_class.set_coefficients = obj_set_coefficients
886 | coeff_class.set_coefficients_by_name = obj_set_coefficients_by_name
887 | coeff_class.get_coefficients_by_name = obj_get_coefficients_by_name
888 |
889 | # }}}
890 |
891 | # {{{ Constraint
892 |
893 | _isl.Constraint.eq_from_names = staticmethod(eq_from_names)
894 | _isl.Constraint.ineq_from_names = staticmethod(ineq_from_names)
895 |
896 | # }}}
897 |
898 | # {{{ BasicSet
899 |
900 | _isl.BasicSet.get_constraints = basic_obj_get_constraints
901 |
902 | # }}}
903 |
904 | # {{{ BasicMap
905 |
906 | _isl.BasicMap.get_constraints = basic_obj_get_constraints
907 |
908 | # }}}
909 |
910 | # {{{ Set
911 |
912 | _isl.Set.get_basic_sets = set_get_basic_sets
913 | _isl.BasicSet.get_basic_sets = set_get_basic_sets
914 |
915 | # }}}
916 |
917 | # {{{ Map
918 |
919 | _isl.Map.get_basic_maps = map_get_basic_maps
920 |
921 | # }}}
922 |
923 |
924 | # {{{ common functionality
925 |
926 | for cls in ALL_CLASSES:
927 | if hasattr(cls, "get_space") and cls is not _isl.Space:
928 | cls.get_id_dict = obj_get_id_dict
929 | cls.get_var_dict = obj_get_var_dict
930 | cls.get_var_ids = obj_get_var_ids
931 | cls.get_var_names = obj_get_var_names
932 |
933 | # }}}
934 |
935 | # {{{ piecewise
936 |
937 | _isl.PwAff.get_pieces = pwaff_get_pieces
938 | _isl.Aff.get_pieces = pwaff_get_pieces
939 | _isl.PwAff.get_aggregate_domain = pw_get_aggregate_domain
940 |
941 | _isl.PwQPolynomial.get_pieces = pwqpolynomial_get_pieces
942 | _isl.PwQPolynomial.get_aggregate_domain = pw_get_aggregate_domain
943 |
944 | # }}}
945 |
946 | _isl.QPolynomial.get_terms = qpolynomial_get_terms
947 |
948 | _isl.PwQPolynomial.eval_with_dict = pwqpolynomial_eval_with_dict
949 |
950 | # {{{ arithmetic
951 |
952 | for expr_like_class in ARITH_CLASSES:
953 | expr_like_class.__add__ = expr_like_add
954 | expr_like_class.__radd__ = expr_like_add
955 | expr_like_class.__sub__ = expr_like_sub
956 | expr_like_class.__rsub__ = expr_like_rsub
957 | expr_like_class.__mul__ = expr_like_mul
958 | expr_like_class.__rmul__ = expr_like_mul
959 | expr_like_class.__neg__ = expr_like_class.neg
960 |
961 | for qpoly_class in [_isl.QPolynomial, _isl.PwQPolynomial]:
962 | qpoly_class.__pow__ = qpoly_class.pow
963 |
964 | for aff_class in [_isl.Aff, _isl.PwAff]:
965 | aff_class.__mod__ = aff_class.mod_val
966 | aff_class.__floordiv__ = expr_like_floordiv
967 |
968 | # }}}
969 |
970 | # {{{ Val
971 |
972 | val_cls = _isl.Val
973 |
974 | val_cls.__add__ = val_cls.add
975 | val_cls.__radd__ = val_cls.add
976 | val_cls.__sub__ = val_cls.sub
977 | val_cls.__rsub__ = val_rsub
978 | val_cls.__mul__ = val_cls.mul
979 | val_cls.__rmul__ = val_cls.mul
980 | val_cls.__neg__ = val_cls.neg
981 | val_cls.__mod__ = val_cls.mod
982 | val_cls.__bool__ = val_cls.__nonzero__ = val_bool
983 |
984 | val_cls.__lt__ = val_cls.lt
985 | val_cls.__gt__ = val_cls.gt
986 | val_cls.__le__ = val_cls.le
987 | val_cls.__ge__ = val_cls.ge
988 | val_cls.__eq__ = val_cls.eq
989 | val_cls.__ne__ = val_cls.ne
990 |
991 | val_cls.__repr__ = val_repr
992 | val_cls.__str__ = val_cls.to_str
993 | val_cls.to_python = val_to_python
994 |
995 | # }}}
996 |
997 | # {{{ rich comparisons
998 |
999 | for cls in [_isl.BasicSet, _isl.BasicMap, _isl.Set, _isl.Map]:
1000 | cls.__lt__ = obj_lt
1001 | cls.__le__ = obj_le
1002 | cls.__gt__ = obj_gt
1003 | cls.__ge__ = obj_ge
1004 |
1005 | # }}}
1006 |
1007 | for c in [_isl.BasicSet, _isl.BasicMap, _isl.Set, _isl.Map]:
1008 | c.project_out_except = obj_project_out_except
1009 | c.add_constraints = obj_add_constraints
1010 |
1011 | for c in [_isl.BasicSet, _isl.Set]:
1012 | c.eliminate_except = obj_eliminate_except
1013 |
1014 |
1015 | _add_functionality()
1016 |
--------------------------------------------------------------------------------
/islpy/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inducer/islpy/b9feb05b776701aacf6de52dc5a5c9be1606a0c6/islpy/py.typed
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | build-backend = "scikit_build_core.build"
3 | requires = [
4 | "scikit-build-core >=0.9.3",
5 | "nanobind >=1.9.2",
6 | "pcpp",
7 |
8 | # stubgen uses @override :/
9 | "typing_extensions>=4.5",
10 | ]
11 |
12 | [project]
13 | name = "islpy"
14 | version = "2025.2"
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 | ]
56 |
57 | # FIXME: Comment out before committing
58 | # Use with --no-build-isolation for fast development builds
59 | # build-dir = "build/{wheel_tag}"
60 |
61 | [tool.ruff]
62 | preview = true
63 | exclude = [
64 | "isl",
65 | "aksetup_helper.py"
66 | ]
67 | target-version = "py310"
68 |
69 |
70 | [tool.ruff.lint]
71 | extend-select = [
72 | "B", # flake8-bugbear
73 | "C", # flake8-comprehensions
74 | "E", # pycodestyle
75 | "F", # pyflakes
76 | "G", # flake8-logging-format
77 | "I", # flake8-isort
78 | "N", # pep8-naming
79 | "NPY", # numpy
80 | "Q", # flake8-quotes
81 | "UP", # pyupgrade
82 | "RUF", # ruff
83 | "W", # pycodestyle
84 | "TC",
85 | ]
86 | extend-ignore = [
87 | "C90", # McCabe complexity
88 | "E221", # multiple spaces before operator
89 | "E226", # missing whitespace around arithmetic operator
90 | "E402", # module-level import not at top of file
91 | "UP031", # use f-strings instead of %
92 | "UP032", # use f-strings instead of .format
93 | ]
94 |
95 | [tool.ruff.lint.flake8-quotes]
96 | docstring-quotes = "double"
97 | inline-quotes = "double"
98 | multiline-quotes = "double"
99 |
100 | [tool.ruff.lint.isort]
101 | combine-as-imports = true
102 | known-local-folder = [
103 | "islpy",
104 | ]
105 | lines-after-imports = 2
106 |
107 | [tool.ruff.lint.per-file-ignores]
108 | "islpy/*.pyi" = [
109 | "N801", "N802", "E501", "I001", "F401", "E202", "E203", "Q000",
110 | "RUF012"
111 | ]
112 |
113 | [tool.cibuildwheel]
114 | # nanobind does not support Py<3.8
115 | # i686 does not have enough memory for LTO to complete
116 | skip = ["pp*", "cp3[6789]-*", "*_i686"]
117 |
118 | test-requires = "pytest"
119 | test-command = "pytest {project}/test"
120 |
121 | [tool.cibuildwheel.macos.environment]
122 | # Needed for full C++17 support
123 | MACOSX_DEPLOYMENT_TARGET = "10.14"
124 |
125 |
126 | [tool.basedpyright]
127 | reportImplicitStringConcatenation = "none"
128 | reportUnnecessaryIsInstance = "none"
129 | reportUnusedCallResult = "none"
130 | reportExplicitAny = "none"
131 | reportUnreachable = "none"
132 |
133 | # This reports even cycles that are qualified by 'if TYPE_CHECKING'. Not what
134 | # we care about at this moment.
135 | # https://github.com/microsoft/pyright/issues/746
136 | reportImportCycles = "none"
137 | pythonVersion = "3.10"
138 | pythonPlatform = "All"
139 |
140 | [[tool.basedpyright.executionEnvironments]]
141 | root = "islpy/_monkeypatch.py"
142 | reportUnknownArgumentType = "hint"
143 | reportAttributeAccessIssue = "none"
144 | reportPrivateUsage = "none"
145 |
146 | [[tool.basedpyright.executionEnvironments]]
147 | root = "islpy/_isl.pyi"
148 | reportUnannotatedClassAttribute = "none"
149 | reportImplicitOverride = "none"
150 |
151 |
--------------------------------------------------------------------------------
/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.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 | // vim: foldmethod=marker
347 |
--------------------------------------------------------------------------------
/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 | wrap_pw_aff.def(py::init_implicit());
116 |
117 | MAKE_WRAP(union_pw_aff, UnionPwAff);
118 | wrap_union_pw_aff.def(py::init_implicit());
119 | wrap_union_pw_aff.def(py::init_implicit());
120 |
121 | MAKE_WRAP(multi_id, MultiId);
122 |
123 | MAKE_WRAP(multi_aff, MultiAff);
124 |
125 | MAKE_WRAP(pw_multi_aff, PwMultiAff);
126 | wrap_pw_multi_aff.def(py::init_implicit());
127 |
128 | MAKE_WRAP(union_pw_multi_aff, UnionPwMultiAff);
129 | wrap_union_pw_multi_aff.def(py::init_implicit());
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 | wrap_local_space.def(py::init_implicit());
173 |
174 | #include "gen-expose-part1.inc"
175 | }
176 |
--------------------------------------------------------------------------------
/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 |
12 | MAKE_WRAP(basic_map, BasicMap);
13 |
14 | MAKE_WRAP(set, Set);
15 | wrap_set.def(py::init_implicit());
16 |
17 | MAKE_WRAP(map, Map);
18 | wrap_map.def(py::init_implicit());
19 |
20 | MAKE_WRAP(union_set, UnionSet);
21 | wrap_union_set.def(py::init_implicit());
22 |
23 | MAKE_WRAP(union_map, UnionMap);
24 | wrap_union_map.def(py::init_implicit());
25 |
26 | MAKE_WRAP(point, Point);
27 |
28 | MAKE_WRAP(vertex, Vertex);
29 |
30 | MAKE_WRAP(cell, Cell);
31 |
32 | MAKE_WRAP(vertices, Vertices);
33 | MAKE_WRAP(stride_info, StrideInfo);
34 |
35 | #include "gen-expose-part2.inc"
36 | }
37 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/stubgen/stubgen.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import importlib
3 | import sys
4 | from collections.abc import Callable
5 | from pathlib import Path
6 | from typing import TYPE_CHECKING, Any, cast
7 |
8 | from nanobind.stubgen import StubGen as StubGenBase
9 | from typing_extensions import override
10 |
11 |
12 | if TYPE_CHECKING:
13 | import enum
14 |
15 |
16 | class StubGen(StubGenBase):
17 | # can be removed once https://github.com/wjakob/nanobind/pull/1055 is merged
18 | @override
19 | def put_function(self,
20 | fn: Callable[..., Any],
21 | name: str | None = None,
22 | parent: object | None = None
23 | ):
24 | fn_module = getattr(fn, "__module__", None)
25 |
26 | if (name and fn_module
27 | and fn_module != self.module.__name__
28 | and parent is not None):
29 | self.import_object(fn_module, name=None)
30 | rhs = f"{fn_module}.{fn.__qualname__}"
31 | if type(fn) is staticmethod:
32 | rhs = f"staticmethod({rhs})"
33 | self.write_ln(f"{name} = {rhs}\n")
34 |
35 | return
36 |
37 | super().put_function(fn, name, parent)
38 |
39 | @override
40 | def put(self,
41 | value: object,
42 | name: str | None = None,
43 | parent: object | None = None
44 | ) -> None:
45 | if name == "in_" and parent and parent.__name__ == "dim_type":
46 | # https://github.com/wjakob/nanobind/discussions/1066
47 | self.write_ln(f"{name} = {cast('enum.Enum', value).value}")
48 |
49 | super().put(value, name, parent)
50 |
51 |
52 | def main():
53 | parser = argparse.ArgumentParser()
54 | parser.add_argument("-m", "--module", default="islpy._isl")
55 | parser.add_argument("--exec", nargs="+")
56 | parser.add_argument("--python-path", nargs="+")
57 | parser.add_argument("-o", "--output-dir", default="../islpy")
58 | args = parser.parse_args()
59 | output_path = Path(cast("str", args.output_dir))
60 |
61 | sys.path.extend(cast("list[str]", args.python_path or []))
62 |
63 | mod = importlib.import_module(cast("str", args.module))
64 | for fname in cast("list[str]", args.exec or []):
65 | execdict = {"__name__": "islpy._monkeypatch"}
66 | with open(fname) as inf:
67 | exec(compile(inf.read(), fname, "exec"), execdict)
68 |
69 | sg = StubGen(
70 | module=mod,
71 | quiet=True,
72 | recursive=False,
73 | include_docstrings=False,
74 | )
75 | sg.put(mod)
76 | prefix_lines = "\n".join([
77 | "from collections.abc import Callable",
78 | ])
79 | with open(output_path / "_isl.pyi", "w") as outf:
80 | outf.write(f"{prefix_lines}\n{sg.get()}")
81 |
82 |
83 | if __name__ == "__main__":
84 | main()
85 |
--------------------------------------------------------------------------------
/test/test_isl.py:
--------------------------------------------------------------------------------
1 | __copyright__ = "Copyright (C) 2011-15 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 | import pytest
24 |
25 | import islpy as isl
26 |
27 |
28 | def test_basics():
29 | dt = isl.dim_type
30 |
31 | ctx = isl.Context()
32 | space = isl.Space.create_from_names(ctx, set=["a", "b"])
33 |
34 | bset = (isl.BasicSet.universe(space)
35 | .add_constraint(isl.Constraint.eq_from_names(
36 | space, {"a": -1, "b": 2}))
37 | .add_constraint(isl.Constraint.ineq_from_names(
38 | space, {"a": 1, 1: -10}))
39 | .add_constraint(isl.Constraint.ineq_from_names(
40 | space, {"a": -1, 1: 42}))
41 | .project_out(dt.set, 1, 1))
42 |
43 | bset2 = isl.BasicSet( # noqa
44 | "{[i] : exists (a : i = 2a and i >= 10 and i <= 42)}",
45 | context=ctx)
46 |
47 | points = []
48 | bset.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(bset):
164 | result = 1
165 |
166 | for i in range(bset.dim(isl.dim_type.set)):
167 | dmax = bset.dim_max(i)
168 | dmin = bset.dim_min(i)
169 |
170 | length = isl.PwQPolynomial.from_pw_aff(dmax - dmin + 1)
171 |
172 | result = result * length
173 |
174 | return result
175 |
176 | counts = [count(a)]
177 |
178 | if hasattr(a, "card"):
179 | counts.append(a.card())
180 |
181 | for pwq in counts:
182 | print("EVAL", pwq, "=", pwq.eval_with_dict({"n": 10}))
183 |
184 | print(counts)
185 |
186 | assert counts[0].eval_with_dict({"n": 10}) == 100
187 | if hasattr(a, "card"):
188 | assert counts[1].eval_with_dict({"n": 10}) == 55
189 |
190 |
191 | def test_eval_pw_qpolynomial():
192 | pwaff = isl.PwAff("[n] -> { [(0)] : n <= 4 and n >= 1; "
193 | "[(-1 + n - floor((3n)/4))] : n >= 5 }")
194 |
195 | pwq = isl.PwQPolynomial.from_pw_aff(pwaff)
196 |
197 | print(pwq.eval_with_dict({"n": 10}))
198 |
199 | assert pwq.eval_with_dict({"n": 10}) == 2
200 |
201 |
202 | def test_schedule():
203 | schedule = isl.Map("{S[t,i,j] -> [t,i,j]: 0 < t < 20 and 0 < i < j < 100}")
204 | accesses = isl.Map("{S[t,i,j] -> bar[t%2, i+1, j-1]}")
205 | context = isl.Set("{:}")
206 | build = isl.AstBuild.from_context(context)
207 |
208 | def callback(node, build):
209 | schedulemap = build.get_schedule()
210 | accessmap = accesses.apply_domain(schedulemap)
211 | aff = isl.PwMultiAff.from_map(isl.Map.from_union_map(accessmap))
212 | access = build.call_from_pw_multi_aff(aff)
213 | return isl.AstNode.alloc_user(access)
214 |
215 | build, _callback_handle = build.set_at_each_domain(callback)
216 |
217 | ast = build.ast_from_schedule(schedule)
218 |
219 | def cb_print_user(printer, options, node):
220 | print("Callback user called")
221 | printer = printer.print_str("Callback user")
222 | return printer
223 |
224 | def cb_print_for(printer, options, node):
225 | print("Callback for called")
226 | printer = printer.print_str("Callback For")
227 | return printer
228 |
229 | opts = isl.AstPrintOptions.alloc(isl.DEFAULT_CONTEXT)
230 | opts, _cb_print_user_handle = opts.set_print_user(cb_print_user)
231 | opts, _cb_print_for_handle = opts.set_print_for(cb_print_for)
232 |
233 | printer = isl.Printer.to_str(isl.DEFAULT_CONTEXT)
234 | printer = printer.set_output_format(isl.format.C)
235 | printer.print_str("// Start\n")
236 | printer = ast.print_(printer, opts)
237 | printer.print_str("// End")
238 |
239 | print(printer.get_str())
240 |
241 |
242 | def test_union_map():
243 | d = isl.UnionSet("[start, num] -> {S[i,j] : start <= i,j < start + num}")
244 | s = isl.UnionMap("{S[i,j] -> [i,j]}").intersect_domain(d)
245 | aw = isl.UnionMap("{S[i,j] -> B[1024 i + j]}")
246 | aw.compute_flow(aw, aw, s)
247 |
248 |
249 | def test_schedule_dump():
250 | ctx = isl.Context()
251 | s = isl.UnionSet.read_from_str(ctx,
252 | "{ S_2[i, j, k] : i <= 99 and i >= 0; S_3[i] : "
253 | "i <= 99 and i >= 0; S_0[]; S_1[i] : i <= 99 and i >= 0 }")
254 | cst = isl.ScheduleConstraints.on_domain(s)
255 | schedule = isl.ScheduleConstraints.compute_schedule(cst)
256 | schedule.dump()
257 |
258 |
259 | def test_from_union_map():
260 | ctx = isl.Context()
261 | m = isl.UnionMap.read_from_str(ctx,
262 | "[m, n] -> { S_0[] -> [0, 0, 0, 0]; S_1[i] -> [i, 1, 0, 0]; S_3[i] -> "
263 | "[1 + i, 3, 0, 0]; S_2[i, j, k] -> [i, 2, j, k] : "
264 | "j <= -1 + m and j >= 0 and k <= -1 + n and k >= 0 }")
265 |
266 | isl.MultiUnionPwAff.from_union_map(m)
267 |
268 |
269 | def test_get_schedule_map():
270 | ctx = isl.Context()
271 | ss = isl.UnionSet.read_from_str(
272 | ctx, "[m, n] -> { S_2[i, j, k] : "
273 | "j <= -1 + m and j >= 0 and k <= -1 + n and k >= 0 }")
274 | cst1 = isl.ScheduleConstraints.on_domain(ss)
275 | sub_schedule = isl.ScheduleConstraints.compute_schedule(cst1)
276 | sub_schedule.get_map()
277 |
278 |
279 | def test_codegen():
280 | # courtesy of Marek Pałkowski
281 |
282 | def isl_ast_codegen(S): # noqa: N803
283 | b = isl.AstBuild.from_context(isl.Set("{:}"))
284 | m = isl.Map.from_domain_and_range(S, S)
285 | m = isl.Map.identity(m.get_space())
286 | m = isl.Map.from_domain(S)
287 | ast = b.ast_from_schedule(m)
288 | p = isl.Printer.to_str(isl.DEFAULT_CONTEXT)
289 | p = p.set_output_format(isl.format.C)
290 | p.flush()
291 | p = p.print_ast_node(ast)
292 | return p.get_str()
293 |
294 | s = isl.Set("[n,m] -> { [i,j] : 0 <= i <= n and i <= j <= m }")
295 | print(isl_ast_codegen(s))
296 |
297 |
298 | def test_make_zero_and_vars():
299 | v = isl.make_zero_and_vars("i,j,k", "n")
300 |
301 | myset = (
302 | v[0].le_set(v["i"] + v["j"])
303 | & (v["i"] + v["j"]).lt_set(v["n"])
304 | & (v[0].le_set(v["i"]))
305 | & (v["i"].le_set(13 + v["n"]))
306 | )
307 |
308 | print(myset)
309 |
310 |
311 | def test_affs_from_space():
312 | s = isl.Set("[n] -> {[i,j,k]: 0<=i,j,k "
337 | "{ [i0, i1, i2] : 0 <= i0 < n1 and 0 and 0 <= i2 <= 15 }")
338 |
339 |
340 | def test_lexmin():
341 | print(isl.Set("""{ [s] : exists a,b,c :
342 | 0 <= a <= 5 and 1 <= b <= 4 and 2 <= c <= 7 and
343 | ((2 <= b and b <= 3) implies (a <= 1 or a >= 3)) and
344 | ((not (c < 5 or b > 3)) implies (a > 2 and c < 3)) and s = a + b + c }
345 | """).lexmin())
346 |
347 |
348 | def test_align_spaces():
349 | m1 = isl.BasicMap("[m,n] -> {[i,j,k]->[l,o]:}")
350 | m2 = isl.BasicMap("[m,n] -> {[j,k,l,i]->[o]:}")
351 |
352 | result = isl.align_spaces(m1, m2)
353 | assert result.get_var_dict() == m2.get_var_dict()
354 |
355 | a1 = isl.Aff("[t0, t1, t2] -> { [(32)] }")
356 | a2 = isl.Aff("[t1, t0] -> { [(0)] }")
357 |
358 | with pytest.raises(isl.Error):
359 | a1_aligned = isl.align_spaces(a1, a2)
360 |
361 | a1_aligned = isl.align_spaces(a1, a2, obj_bigger_ok=True)
362 | a2_aligned = isl.align_spaces(a2, a1)
363 |
364 | assert a1_aligned == isl.Aff("[t1, t0, t2] -> { [(32)] }")
365 | assert a2_aligned == isl.Aff("[t1, t0, t2] -> { [(0)] }")
366 |
367 |
368 | def test_pass_numpy_int():
369 | np = pytest.importorskip("numpy")
370 |
371 | s = isl.BasicMap("{[i,j]: 0<=i,j<15}")
372 | c0 = s.get_constraints()[0]
373 |
374 | c1 = c0.set_constant_val(np.int32(5))
375 | print(c1)
376 |
377 |
378 | def test_isl_align_two():
379 | a1 = isl.Aff("[t0, t1, t2] -> { [(32)] }")
380 | a2 = isl.Aff("[t1, t0] -> { [(0)] }")
381 |
382 | a1_aligned, a2_aligned = isl.align_two(a1, a2)
383 | assert a1_aligned == isl.Aff("[t1, t0, t2] -> { [(32)] }")
384 | assert a2_aligned == isl.Aff("[t1, t0, t2] -> { [(0)] }")
385 |
386 | b1 = isl.BasicSet("[n0, n1, n2] -> { [i0, i1] : }")
387 | b2 = isl.BasicSet("[n0, n2, n1, n3] -> { [i1, i0, i2] : }")
388 |
389 | b1_aligned, b2_aligned = isl.align_two(b1, b2)
390 | assert b1_aligned == isl.BasicSet("[n0, n2, n1, n3] -> { [i1, i0, i2] : }")
391 | assert b2_aligned == isl.BasicSet("[n0, n2, n1, n3] -> { [i1, i0, i2] : }")
392 |
393 |
394 | def test_bound():
395 | print(isl.PwQPolynomial("""[n, m] -> {[i, j] -> i * m + j :
396 | 0 <= i < n and 0 <= j < m}""").bound(isl.fold.min))
397 | print(isl.PwQPolynomial("""[n, m] -> {[i, j] -> i * m + j :
398 | 0 <= i < n and 0 <= j < m}""").bound(isl.fold.max))
399 |
400 |
401 | def test_copy_context():
402 | ctx = isl.Context()
403 | import copy
404 | assert not ctx._wraps_same_instance_as(copy.copy(ctx))
405 | assert not isl.DEFAULT_CONTEXT._wraps_same_instance_as(copy.copy(ctx))
406 |
407 |
408 | def test_ast_node_list_free():
409 | # from https://github.com/inducer/islpy/issues/21
410 | # by Cambridge Yang
411 |
412 | ctx = isl.Context()
413 | schedule_map = isl.UnionMap.read_from_str(
414 | ctx, "[N] -> { S0[i] -> [i, 0] : "
415 | "0 <= i < N; S1[i] -> [i, 1] : 0 <= i < N }")
416 | ast_build = isl.AstBuild.from_context(isl.Set.read_from_str(ctx, "[N] -> { : }"))
417 | ast = ast_build.node_from_schedule_map(schedule_map)
418 |
419 | print(ast.to_C_str())
420 | # Prints below code:
421 | # for (int c0 = 0; c0 < N; c0 += 1) {
422 | # S0(c0);
423 | # S1(c0);
424 | # }
425 |
426 | # we have S0 and S1 in a ast_node_block, which holds "children" of type
427 | # ASTNodeList
428 | body = ast.for_get_body()
429 | assert body.get_type() == isl.ast_node_type.block
430 |
431 | body.block_get_children()
432 |
433 |
434 | def test_union_casts():
435 | # https://github.com/inducer/islpy/issues/29
436 | s1 = isl.UnionSet("{[0]}")
437 | s2 = isl.BasicSet("{[1]}")
438 |
439 | s2.union(s1) # works fine
440 | s1.union(s2) # did not work while #29 was not fixed
441 |
442 | assert s2.union(s1) == s1.union(s2)
443 |
444 |
445 | def test_remove_map_if_callback():
446 | ctx = isl.Context()
447 |
448 | umap = isl.UnionMap.read_from_str(ctx, "{A[0] -> [1]; B[1] -> [2]}")
449 |
450 | umap1 = umap.remove_map_if(lambda m: False)
451 | assert umap1 == umap, "map should not change"
452 |
453 | umap2 = umap.remove_map_if(lambda m:
454 | m.get_tuple_name(isl.dim_type.in_) == "B")
455 | assert umap2 == isl.UnionMap.read_from_str(ctx, "{A[0] -> [1]}")
456 |
457 |
458 | def test_remove_map_if_callback_exc():
459 | pytest.skip("https://github.com/inducer/islpy/pull/33#issuecomment-705165253")
460 | ctx = isl.Context()
461 |
462 | umap = isl.UnionMap.read_from_str(ctx, "{A[0] -> [1]; B[1] -> [2]}")
463 |
464 | def callback_throws_exception(m):
465 | raise AssertionError()
466 |
467 | with pytest.raises(isl.Error):
468 | umap3 = umap.remove_map_if(callback_throws_exception)
469 | del umap3
470 |
471 |
472 | def test_sched_constraints_set_validity():
473 | domain = isl.UnionSet("[n] -> { A[i] : 0 <= i < n; B[i] : 0 <= i < n }")
474 | validity = isl.UnionMap("[n] -> { A[i] -> B[i] : 0 <= i < n }")
475 | sc = isl.ScheduleConstraints.on_domain(domain)
476 |
477 | sc = sc.set_validity(validity)
478 | validity2 = sc.get_validity()
479 |
480 | print(validity)
481 | print(validity2)
482 |
483 | assert str(validity) == str(validity2)
484 |
485 |
486 | if __name__ == "__main__":
487 | import sys
488 | if len(sys.argv) > 1:
489 | exec(sys.argv[1])
490 | else:
491 | from pytest import main
492 | main([__file__])
493 |
--------------------------------------------------------------------------------