├── .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 | --------------------------------------------------------------------------------